[xiph-commits] r17093 - in trunk: . cronus cronus/com cronus/com/fluendo cronus/com/fluendo/codecs cronus/com/fluendo/examples cronus/com/fluendo/jkate cronus/com/fluendo/jst cronus/com/fluendo/jtiger cronus/com/fluendo/player cronus/com/fluendo/plugin cronus/com/fluendo/utils cronus/com/meviatronic cronus/com/meviatronic/zeus cronus/com/meviatronic/zeus/castor cronus/com/meviatronic/zeus/helen cronus/com/meviatronic/zeus/pollux cronus/org cronus/org/xiph cronus/org/xiph/ogg

mike at svn.xiph.org mike at svn.xiph.org
Sat Mar 27 09:19:37 PDT 2010


Author: mike
Date: 2010-03-27 09:19:37 -0700 (Sat, 27 Mar 2010)
New Revision: 17093

Added:
   trunk/cronus/
   trunk/cronus/README.txt
   trunk/cronus/com/
   trunk/cronus/com/fluendo/
   trunk/cronus/com/fluendo/codecs/
   trunk/cronus/com/fluendo/codecs/SmokeCodec.java
   trunk/cronus/com/fluendo/examples/
   trunk/cronus/com/fluendo/examples/OggPerf.java
   trunk/cronus/com/fluendo/examples/OggTheoraPerf.java
   trunk/cronus/com/fluendo/examples/Player.java
   trunk/cronus/com/fluendo/jkate/
   trunk/cronus/com/fluendo/jkate/Bitmap.java
   trunk/cronus/com/fluendo/jkate/Bitwise.java
   trunk/cronus/com/fluendo/jkate/Buffer.java
   trunk/cronus/com/fluendo/jkate/Color.java
   trunk/cronus/com/fluendo/jkate/Comment.java
   trunk/cronus/com/fluendo/jkate/Curve.java
   trunk/cronus/com/fluendo/jkate/Decode.java
   trunk/cronus/com/fluendo/jkate/Event.java
   trunk/cronus/com/fluendo/jkate/FontMapping.java
   trunk/cronus/com/fluendo/jkate/FontRange.java
   trunk/cronus/com/fluendo/jkate/Info.java
   trunk/cronus/com/fluendo/jkate/KateBadPacketException.java
   trunk/cronus/com/fluendo/jkate/KateBitmapType.java
   trunk/cronus/com/fluendo/jkate/KateCurveType.java
   trunk/cronus/com/fluendo/jkate/KateException.java
   trunk/cronus/com/fluendo/jkate/KateMarkupType.java
   trunk/cronus/com/fluendo/jkate/KateMotionMapping.java
   trunk/cronus/com/fluendo/jkate/KateMotionSemantics.java
   trunk/cronus/com/fluendo/jkate/KateSpaceMetric.java
   trunk/cronus/com/fluendo/jkate/KateTextDirectionality.java
   trunk/cronus/com/fluendo/jkate/KateTextEncoding.java
   trunk/cronus/com/fluendo/jkate/KateWrapMode.java
   trunk/cronus/com/fluendo/jkate/Motion.java
   trunk/cronus/com/fluendo/jkate/Palette.java
   trunk/cronus/com/fluendo/jkate/RLE.java
   trunk/cronus/com/fluendo/jkate/Region.java
   trunk/cronus/com/fluendo/jkate/Result.java
   trunk/cronus/com/fluendo/jkate/State.java
   trunk/cronus/com/fluendo/jkate/Style.java
   trunk/cronus/com/fluendo/jkate/Tracker.java
   trunk/cronus/com/fluendo/jst/
   trunk/cronus/com/fluendo/jst/Buffer.java
   trunk/cronus/com/fluendo/jst/Bus.java
   trunk/cronus/com/fluendo/jst/BusHandler.java
   trunk/cronus/com/fluendo/jst/BusSyncHandler.java
   trunk/cronus/com/fluendo/jst/Caps.java
   trunk/cronus/com/fluendo/jst/CapsListener.java
   trunk/cronus/com/fluendo/jst/Clock.java
   trunk/cronus/com/fluendo/jst/ClockProvider.java
   trunk/cronus/com/fluendo/jst/Element.java
   trunk/cronus/com/fluendo/jst/ElementFactory.java
   trunk/cronus/com/fluendo/jst/Event.java
   trunk/cronus/com/fluendo/jst/Format.java
   trunk/cronus/com/fluendo/jst/Message.java
   trunk/cronus/com/fluendo/jst/Object.java
   trunk/cronus/com/fluendo/jst/Pad.java
   trunk/cronus/com/fluendo/jst/PadListener.java
   trunk/cronus/com/fluendo/jst/Pipeline.java
   trunk/cronus/com/fluendo/jst/Query.java
   trunk/cronus/com/fluendo/jst/Sink.java
   trunk/cronus/com/fluendo/jst/SourceInfo.java
   trunk/cronus/com/fluendo/jst/SystemClock.java
   trunk/cronus/com/fluendo/jst/WaitStatus.java
   trunk/cronus/com/fluendo/jtiger/
   trunk/cronus/com/fluendo/jtiger/Item.java
   trunk/cronus/com/fluendo/jtiger/Renderer.java
   trunk/cronus/com/fluendo/jtiger/TigerBitmap.java
   trunk/cronus/com/fluendo/player/
   trunk/cronus/com/fluendo/player/Configure.java
   trunk/cronus/com/fluendo/player/Cortado.java
   trunk/cronus/com/fluendo/player/CortadoPipeline.java
   trunk/cronus/com/fluendo/player/DurationScanner.java
   trunk/cronus/com/fluendo/player/Status.java
   trunk/cronus/com/fluendo/player/StatusListener.java
   trunk/cronus/com/fluendo/plugin/
   trunk/cronus/com/fluendo/plugin/AudioSink.java
   trunk/cronus/com/fluendo/plugin/AudioSinkJ2.java
   trunk/cronus/com/fluendo/plugin/AudioSinkSA.java
   trunk/cronus/com/fluendo/plugin/FakeSink.java
   trunk/cronus/com/fluendo/plugin/HTTPSrc.java
   trunk/cronus/com/fluendo/plugin/JPEGDec.java
   trunk/cronus/com/fluendo/plugin/KateDec.java
   trunk/cronus/com/fluendo/plugin/KateOverlay.java
   trunk/cronus/com/fluendo/plugin/MulawDec.java
   trunk/cronus/com/fluendo/plugin/MultipartDemux.java
   trunk/cronus/com/fluendo/plugin/OggDemux.java
   trunk/cronus/com/fluendo/plugin/OggPayload.java
   trunk/cronus/com/fluendo/plugin/Overlay.java
   trunk/cronus/com/fluendo/plugin/Queue.java
   trunk/cronus/com/fluendo/plugin/Selector.java
   trunk/cronus/com/fluendo/plugin/SmokeDec.java
   trunk/cronus/com/fluendo/plugin/TextOverlay.java
   trunk/cronus/com/fluendo/plugin/TheoraDec.java
   trunk/cronus/com/fluendo/plugin/VideoSink.java
   trunk/cronus/com/fluendo/plugin/VorbisDec.java
   trunk/cronus/com/fluendo/utils/
   trunk/cronus/com/fluendo/utils/Base64Converter.java
   trunk/cronus/com/fluendo/utils/Debug.java
   trunk/cronus/com/fluendo/utils/MemUtils.java
   trunk/cronus/com/meviatronic/
   trunk/cronus/com/meviatronic/zeus/
   trunk/cronus/com/meviatronic/zeus/castor/
   trunk/cronus/com/meviatronic/zeus/castor/AudioReader.java
   trunk/cronus/com/meviatronic/zeus/castor/EndOfMediaException.java
   trunk/cronus/com/meviatronic/zeus/castor/EndOfPacketException.java
   trunk/cronus/com/meviatronic/zeus/castor/Imdct.java
   trunk/cronus/com/meviatronic/zeus/castor/Output.java
   trunk/cronus/com/meviatronic/zeus/castor/README.txt
   trunk/cronus/com/meviatronic/zeus/castor/VorbisDecoder.java
   trunk/cronus/com/meviatronic/zeus/helen/
   trunk/cronus/com/meviatronic/zeus/helen/BaseTag.java
   trunk/cronus/com/meviatronic/zeus/helen/Converter.java
   trunk/cronus/com/meviatronic/zeus/helen/OggTag.java
   trunk/cronus/com/meviatronic/zeus/helen/README.txt
   trunk/cronus/com/meviatronic/zeus/helen/Tag.java
   trunk/cronus/com/meviatronic/zeus/helen/TagException.java
   trunk/cronus/com/meviatronic/zeus/pollux/
   trunk/cronus/com/meviatronic/zeus/pollux/MemoryImageSource.java
   trunk/cronus/com/meviatronic/zeus/pollux/Output.java
   trunk/cronus/com/meviatronic/zeus/pollux/README.txt
   trunk/cronus/com/meviatronic/zeus/pollux/TheoraDecoder.java
   trunk/cronus/com/meviatronic/zeus/pollux/VideoReader.java
   trunk/cronus/org/
   trunk/cronus/org/xiph/
   trunk/cronus/org/xiph/ogg/
   trunk/cronus/org/xiph/ogg/Packet.java
   trunk/cronus/org/xiph/ogg/Page.java
   trunk/cronus/org/xiph/ogg/StreamState.java
   trunk/cronus/org/xiph/ogg/SyncState.java
Log:
Initial import ...

Added: trunk/cronus/README.txt
===================================================================
--- trunk/cronus/README.txt	                        (rev 0)
+++ trunk/cronus/README.txt	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,24 @@
+	Cronus, a fast Theora/Vorbis player created by Michael Scheerer
+	and based on the Jst multimedia framework developed from Wim Taymans.
+	Cronus player (c) 2009 Michael Scheerer www.meviatronic.com
+	Jst framework (c) 2004 Fluendo S.L.
+
+	Many thanks to
+	Monty <monty at xiph.org> and
+	The XIPHOPHORUS Company http://www.xiph.org/ .
+	
+	Cronus is intendend to be the successor of the Cortado Theora/Vorbis player
+	and predecessor of the Zeus player.
+	The base of Cronus is the Jst (Java port of GStreamer 0.10) multimedia framework
+	developed from Wim Taymans and new Theora/Vorbis/Comment decoders.
+	
+	Advantages of Cronus over the current Cortado player:
+	- better decoders with better performance.
+ 	- much lower performance consumption:
+ 	 100 Mbyte instead of 140 Mbyte after playing 10 minutes
+ 	 of a 640 * 320 Theora video (BigBugBunny.ogv).
+ 	
+ 	See:
+ 	https://svn.xiph.org/trunk/cronus/com/meviatronic/zeus/helen.README.txt
+ 	https://svn.xiph.org/trunk/cronus/com/meviatronic/zeus/castor.README.txt
+ 	https://svn.xiph.org/trunk/cronus/com/meviatronic/zeus/pollux.README.txt

Added: trunk/cronus/com/fluendo/codecs/SmokeCodec.java
===================================================================
--- trunk/cronus/com/fluendo/codecs/SmokeCodec.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/codecs/SmokeCodec.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,174 @@
+/* Smoke Codec
+ * Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.codecs;
+
+import java.awt.*;
+
+public class SmokeCodec {
+  private static final int IDX_TYPE       = 0;
+  private static final int IDX_WIDTH      = 1;
+  private static final int IDX_HEIGHT     = 3;
+  private static final int IDX_FPS_NUM    = 5;
+  private static final int IDX_FPS_DENOM  = 9;
+  private static final int IDX_FLAGS      = 13;
+  private static final int IDX_NUM_BLOCKS = 14;
+  private static final int IDX_SIZE       = 16;
+  private static final int IDX_BLOCKS     = 18;
+  private static final int OFFS_PICT      = 18;
+
+  private Image reference;
+  private MediaTracker mt;
+  private Component component;
+  private Toolkit toolkit;
+
+  public static final int KEYFRAME = (1<<0);
+
+  public int type;
+  public int width, height;
+  public int fps_num, fps_denom;
+  public int flags;
+  public int size;
+  public int blocks;
+
+  public SmokeCodec(Component comp, MediaTracker tracker) {
+    component = comp;
+    toolkit = comp.getToolkit();
+    mt = tracker;
+  }
+
+  public int parseHeader (byte[] in, int offset, int length) {
+    short b1, b2, b3, b4; 
+
+    type  = in[IDX_TYPE+offset];
+
+    b1 = in[IDX_WIDTH+offset];   if (b1<0) b1 += 256;
+    b2 = in[IDX_WIDTH+1+offset]; if (b2<0) b2 += 256;
+    width = (b1 << 8) | b2;
+    b1 = in[IDX_HEIGHT+offset];   if (b1<0) b1 += 256;
+    b2 = in[IDX_HEIGHT+1+offset]; if (b2<0) b2 += 256;
+    height = (b1 << 8) | b2;
+
+    b1 = in[IDX_FPS_NUM+offset];   if (b1<0) b1 += 256;
+    b2 = in[IDX_FPS_NUM+1+offset]; if (b2<0) b2 += 256;
+    b3 = in[IDX_FPS_NUM+2+offset]; if (b3<0) b3 += 256;
+    b4 = in[IDX_FPS_NUM+3+offset]; if (b4<0) b4 += 256;
+    fps_num = (b1<<24) | (b2<<16) | (b3<<8) | b4;
+    b1 = in[IDX_FPS_DENOM+offset];   if (b1<0) b1 += 256;
+    b2 = in[IDX_FPS_DENOM+1+offset]; if (b2<0) b2 += 256;
+    b3 = in[IDX_FPS_DENOM+2+offset]; if (b3<0) b3 += 256;
+    b4 = in[IDX_FPS_DENOM+3+offset]; if (b4<0) b4 += 256;
+    fps_denom = (b1<<24) | (b2<<16) | (b3<<8) | b4;
+
+    flags  = in[IDX_FLAGS+offset];
+    
+    b1 = in[IDX_SIZE+offset]; if (b1<0) b1 += 256;
+    b2 = in[IDX_SIZE+offset]; if (b2<0) b2 += 256;
+    size = (b1 << 8) | b2;
+
+    b1 = in[IDX_NUM_BLOCKS+offset];   if (b1<0) b1 += 256;
+    b2 = in[IDX_NUM_BLOCKS+1+offset]; if (b2<0) b2 += 256;
+    blocks = (b1 << 8) | b2;
+
+    return 0;
+  }
+  
+  public Image decode (byte[] in, int offset, int length) 
+  { 
+    int b1,b2; 
+
+    parseHeader(in, offset, length);
+
+    boolean keyframe = ((flags & KEYFRAME) != 0);
+
+    if (reference == null && !keyframe) {
+      return null;
+    }
+
+    //System.out.println("blocks: "+blocks+" width "+width+" height "+height);
+
+    int imgoff = blocks*2+OFFS_PICT;
+
+    //System.out.println("decoding "+blocks+" "+imgoff+" "+(length-imgoff)+" "+keyframe);
+    
+    Image src = null;
+    try {
+      src = toolkit.createImage(in, imgoff+offset, length-imgoff);
+    }
+    catch (Exception e) {e.printStackTrace();}
+
+    if (src == null) {
+      System.out.println("failed");
+      return null;
+    }
+      
+    try {
+      mt.addImage(src, 0);
+      mt.waitForID(0);
+      mt.removeImage(src, 0);
+    }
+    catch (Exception e) {e.printStackTrace();}
+
+    if (reference == null || keyframe) {
+      reference = src;
+    }
+    else {
+      if (blocks > 0 ) {
+        int src_w = src.getWidth(null);
+        int src_h = src.getHeight(null);
+        int blockptr = 0;
+        int pos, i, j, x, y;
+
+        Graphics refgfx;
+
+	offset += IDX_BLOCKS;
+
+	Image newref = component.createImage(width, height);
+	refgfx = newref.getGraphics();
+	refgfx.drawImage(reference, 0, 0, null);
+	reference = newref;
+
+        for (i=0; i<src_h; i+=16) {
+          for (j=0; j<src_w; j+=16) {
+	    pos = blockptr*2+offset;
+            b1 = in[pos];   if (b1<0) b1 += 256;
+            b2 = in[pos+1]; if (b2<0) b2 += 256;
+	    pos = (b1 << 8) | b2;
+
+            //System.out.println(i+" "+j+" "+pos);
+
+	    x = (pos % (width/16)) * 16;
+	    y = (pos / (width/16)) * 16;
+
+	    refgfx.drawImage(src, 
+			   x, y, x+16, y+16, 
+			   j, i, j+16, i+16, 
+			   null);
+
+	    blockptr++;
+	    if (blockptr >= blocks)
+	      break;
+          }
+        }
+      }
+    }
+    //System.out.println("decoded "+blocks+" "+imgoff+" "+(length-imgoff)+" "+keyframe+" "+reference);
+    return reference;
+  }
+}

Added: trunk/cronus/com/fluendo/examples/OggPerf.java
===================================================================
--- trunk/cronus/com/fluendo/examples/OggPerf.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/examples/OggPerf.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,168 @@
+/* Cortado - a video player java applet
+ * Copyright (C) 2004 Fluendo S.L.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.examples;
+
+import java.io.*;
+import java.util.*;
+import org.xiph.ogg.*;
+
+public class OggPerf
+{
+  private static final int BUFFSIZE = 8192;
+
+  private InputStream inputStream;
+
+  private SyncState oy;
+  private Vector streams;
+  private boolean stopping;
+
+  class OggStream {
+    public int serialno;
+    public StreamState os;
+    public boolean bos;
+
+    public OggStream (int serial) {
+      serialno = serial;
+      os = new StreamState();
+      os.init(serial);
+      os.reset();
+      bos = true;
+    }
+  }
+
+  public OggPerf (InputStream is) {
+    inputStream = is;
+
+    oy = new SyncState();
+    streams = new Vector();
+    stopping = false;
+  }
+
+  public static void main(String[] args) {
+    try {
+      int run = 10;
+
+      long start = System.currentTimeMillis();
+      while (run-- > 0) {
+        OggPerf perf = new OggPerf(new FileInputStream(args[0]));
+
+        perf.start();
+      }
+      long end = System.currentTimeMillis();
+
+      System.out.println("ellapsed: "+(end-start));
+    }
+    catch (Exception e) {
+      e.printStackTrace();
+    }
+  }
+
+  public void start() {
+    int res;
+    Page og;
+    Packet op;
+
+    System.out.println("started ogg reader");
+
+    og = new Page();
+    op = new Packet();
+
+    try {
+      while (!stopping) {
+        int index = oy.buffer(BUFFSIZE);
+        //System.out.println("reading "+index+" "+BUFFSIZE);
+        int read = inputStream.read(oy.data, index, BUFFSIZE);
+        //System.out.println("read "+read);
+        if (read < 0)
+          break;
+        oy.wrote(read);
+  
+        while (!stopping) {
+	  res = oy.pageout(og);
+	  //System.out.println("pageout "+res);
+          if (res == 0)
+	    break; // need more data
+          if(res == -1) {
+	    // missing or corrupt data at this page position
+            // no reason to complain; already complained above
+          }
+          else {
+	    int serial = og.serialno();
+	    OggStream stream = null;
+	    for (int i=0; i<streams.size(); i++) {
+	      stream = (OggStream) streams.elementAt(i);
+	      if (stream.serialno == serial)
+	        break;
+	      stream = null;
+	    }
+	    if (stream == null) {
+  	      System.out.println("new stream "+serial);
+	      stream = new OggStream(serial);
+	      streams.addElement(stream);
+	    }
+
+	    //System.out.println("pagein");
+            res = stream.os.pagein(og);
+            if (res < 0) {
+              // error; stream version mismatch perhaps
+              System.err.println("Error reading first page of Ogg bitstream data.");
+              return;
+            }
+	    while (!stopping) {
+	      res = stream.os.packetout(op);
+	      //System.out.println("packetout "+res);
+              if(res == 0)
+	        break; // need more data
+              if(res == -1) {
+	        // missing or corrupt data at this page position
+                // no reason to complain; already complained above
+              }
+              else {
+                // we have a packet.  Decode it
+	        if (stream.bos) {
+	          // typefind
+	          if (op.packetBase[op.packet+1] == 0x76) {
+		    System.out.println ("found vorbis audio");
+	          }
+	          else if (op.packetBase[op.packet+1] == 0x73) {
+		    System.out.println ("found smoke video");
+	          }
+	          else if (op.packetBase[op.packet+1] == 0x74) {
+		    System.out.println ("found theora video");
+		  }
+	          stream.bos = false;
+	        }
+	      }
+  	    }
+	  }
+	}
+      }
+    }
+    catch (Exception e) {
+      e.printStackTrace();
+      stopping = true;
+    }
+    
+    System.out.println("ogg reader done");
+  }
+
+  public void stop() {
+    stopping = true;
+  }
+}

Added: trunk/cronus/com/fluendo/examples/OggTheoraPerf.java
===================================================================
--- trunk/cronus/com/fluendo/examples/OggTheoraPerf.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/examples/OggTheoraPerf.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,222 @@
+/* Cortado - a video player java applet
+ * Copyright (C) 2004 Fluendo S.L.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.examples;
+
+import java.io.*;
+import java.util.*;
+import org.xiph.ogg.*;
+import com.meviatronic.zeus.pollux.*;
+
+public class OggTheoraPerf
+{
+  private static final int BUFFSIZE = 8192;
+
+  private InputStream inputStream;
+
+  private SyncState oy;
+  private Vector streams;
+  private boolean stopping;
+
+  interface Consumer {
+    public void consume (Packet op);
+  }
+
+  class TheoraDec implements Consumer {
+    private VideoReader ti;
+    private int packet = 0;
+   
+    public TheoraDec () {
+      ti = new VideoReader();
+    }
+	  public void consume (Packet op) {
+      //System.out.println ("creating packet");
+      if (packet < 3) {
+		  //System.out.println ("decoding header");
+		  
+		try {
+			/*if(ti.readMediaInformation(op) < 0){
+          		// error case; not a theora header
+          		System.err.println("does not contain Theora video data.");
+          		return;
+			 }*/
+			ti.readMediaInformation(op);
+		} catch (Exception e) {
+			System.err.println("does not contain Theora video data.");
+          	return;
+		}
+        
+		  if (packet == 2) {
+			  // MS: Unneccessary
+          
+          System.out.println("theora dimension: "+ti.codedPictureWidth +"x"+ti.codedPictureHeight);
+          System.out.println("theora aspect: "+ti.aspectRatio.width+"x"+ti.aspectRatio.height);
+          System.out.println("theora framerate: "+ti.frameRate);
+
+        }
+      }
+      else {
+       
+        //if (ts.decodeYUVout(yuv) != 0) {
+          //System.err.println("Error getting the picture.");
+          //return;
+        //}
+      }
+      packet++;
+    }
+  }
+
+  class OggStream {
+    public int serialno;
+    public StreamState os;
+    public boolean bos;
+    public Consumer consumer;
+
+    public OggStream (int serial) {
+      serialno = serial;
+      os = new StreamState();
+      os.init(serial);
+      os.reset();
+      bos = true;
+    }
+  }
+
+  public OggTheoraPerf (InputStream is) {
+    inputStream = is;
+
+    oy = new SyncState();
+    streams = new Vector();
+    stopping = false;
+  }
+
+  public static void main(String[] args) {
+    try {
+      int run = 1;
+
+      long start = System.currentTimeMillis();
+      while (run-- > 0) {
+        OggTheoraPerf perf = new OggTheoraPerf(new FileInputStream(args[0]));
+
+        perf.start();
+      }
+      long end = System.currentTimeMillis();
+
+      System.out.println("ellapsed: "+(end-start));
+    }
+    catch (Exception e) {
+      e.printStackTrace();
+    }
+  }
+
+  public void start() {
+    int res;
+    Page og;
+    Packet op;
+
+    System.out.println("started ogg reader");
+
+    og = new Page();
+    op = new Packet();
+
+    try {
+      while (!stopping) {
+        int index = oy.buffer(BUFFSIZE);
+        //System.out.println("reading "+index+" "+BUFFSIZE);
+        int read = inputStream.read(oy.data, index, BUFFSIZE);
+        //System.out.println("read "+read);
+        if (read < 0)
+          break;
+        oy.wrote(read);
+  
+        while (!stopping) {
+	  res = oy.pageout(og);
+	  //System.out.println("pageout "+res);
+          if (res == 0)
+	    break; // need more data
+          if(res == -1) {
+	    // missing or corrupt data at this page position
+            // no reason to complain; already complained above
+          }
+          else {
+	    int serial = og.serialno();
+	    OggStream stream = null;
+	    for (int i=0; i<streams.size(); i++) {
+	      stream = (OggStream) streams.elementAt(i);
+	      if (stream.serialno == serial)
+	        break;
+	      stream = null;
+	    }
+	    if (stream == null) {
+  	      System.out.println("new stream "+serial);
+	      stream = new OggStream(serial);
+	      streams.addElement(stream);
+	    }
+
+	    //System.out.println("pagein");
+            res = stream.os.pagein(og);
+            if (res < 0) {
+              // error; stream version mismatch perhaps
+              System.err.println("Error reading first page of Ogg bitstream data.");
+              return;
+            }
+	    while (!stopping) {
+	      res = stream.os.packetout(op);
+	      //System.out.println("packetout "+res);
+              if(res == 0)
+	        break; // need more data
+              if(res == -1) {
+	        // missing or corrupt data at this page position
+                // no reason to complain; already complained above
+              }
+              else {
+                // we have a packet.  Decode it
+	        if (stream.bos) {
+	          // typefind
+	          if (op.packetBase[op.packet+1] == 0x76) {
+		    System.out.println ("found vorbis audio");
+	          }
+	          else if (op.packetBase[op.packet+1] == 0x73) {
+		    System.out.println ("found smoke video");
+	          }
+	          else if (op.packetBase[op.packet+1] == 0x74) {
+				  System.out.println ("found theora video");
+				  
+		    stream.consumer = new TheoraDec();
+		  }
+	          stream.bos = false;
+	        }
+		if (stream.consumer != null)
+		  stream.consumer.consume (op);
+	      }
+  	    }
+	  }
+	}
+      }
+    }
+    catch (Exception e) {
+      e.printStackTrace();
+      stopping = true;
+    }
+    
+    System.out.println("ogg reader done");
+  }
+
+  public void stop() {
+    stopping = true;
+  }
+}

Added: trunk/cronus/com/fluendo/examples/Player.java
===================================================================
--- trunk/cronus/com/fluendo/examples/Player.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/examples/Player.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,74 @@
+/* Cortado - a video player java applet
+ * Copyright (C) 2004 Fluendo S.L.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.examples;
+
+import com.fluendo.player.*;
+import java.awt.*;
+
+public class Player extends Frame {
+  private static final long serialVersionUID = 1L;
+Cortado applet;
+
+  public Player(String url) {
+    applet = new Cortado();
+    applet.setSize(352, 270);
+    setSize(352, 270);
+	  
+	applet.setParam ("debug", "0");
+    applet.setParam ("url", url);
+    //applet.setParam ("local", "true");
+    //applet.setParam ("seekable", "true");
+    //applet.setParam ("duration", "0600");
+    applet.setParam ("framerate", "60");
+    applet.setParam ("keepaspect", "true");
+    applet.setParam ("video", "true");
+    applet.setParam ("audio", "true");
+    applet.setParam ("kateIndex", "0");
+    //applet.setParam ("kateLanguage", "en");
+    //applet.setParam ("kateCategory", "SUB");
+    //applet.setParam ("audio", "false");
+    applet.setParam ("bufferSize", "200");
+    applet.setParam ("userId", "wim");
+    applet.setParam ("password", "taymans");
+
+    add(applet);
+    show();
+
+    applet.init();
+    applet.start();
+  }
+
+  public static void main(String args[]) {
+    Player p;
+
+    if (args.length < 1) {
+      System.out.println ("usage: Player <uri>");
+      return;
+    }
+
+    p = new Player(args[0]);
+    
+    synchronized (p) {
+      try {
+        p.wait ();
+      }
+      catch (InterruptedException ie) {}
+    }
+  }
+}

Added: trunk/cronus/com/fluendo/jkate/Bitmap.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/Bitmap.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/Bitmap.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,37 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+/**
+ * A Bitmap definition.
+ */
+public class Bitmap
+{
+  public int width;
+  public int height;
+  public int bpp;
+  public KateBitmapType type;
+  public int palette;
+  public byte[] pixels;
+  public int size;
+  public int x_offset;
+  public int y_offset;
+}

Added: trunk/cronus/com/fluendo/jkate/Bitwise.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/Bitwise.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/Bitwise.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,154 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+import org.xiph.ogg.*;
+
+/**
+ * Various methods to read data from a bitstream.
+ */
+public class Bitwise {
+
+  /**
+   * Read a number of bytes into a buffer.
+   */
+  static void readbuf(Buffer opb, byte[] buf, int len)
+  {
+    for (int i=0; i<len; i++) {
+      buf[i] = (byte)opb.read(8);
+    }
+  }
+
+  /**
+   * Read a little endian 32 bit integer.
+   */
+  static int read32(Buffer opb)
+  {
+    int value;
+
+    value  = opb.read(8);
+    value |= opb.read(8) << 8;
+    value |= opb.read(8) << 16;
+    value |= opb.read(8) << 24;
+
+    return value;
+  }
+
+  /**
+   * Read a variable size integer.
+   * Format defined in the Kate specification.
+   */
+  static int read32v(Buffer opb)
+  {
+    int value;
+
+    value  = opb.read(4);
+    if (value == 15) {
+      int sign = opb.read(1);
+      int bits = opb.read(5)+1;
+      value = opb.read(bits);
+      if (sign != 0) value = -value;
+    }
+
+    return value;
+  }
+
+  /**
+   * Read a little endian 64 bit integer.
+   */
+  static long read64(Buffer opb)
+  {
+    long vl, vh;
+    vl = (long)read32(opb);
+    vh = (long)read32(opb);
+    return vl | (vh<<32);
+  }
+
+  /**
+   * Read a (possibly multiple) warp.
+   * Used to skip over unhandled data from newer (bit still compatible)
+   * bitstream versions.
+   */
+  static int skipWarp(Buffer opb)
+  {
+    while (true) {
+      int bits = read32v(opb);
+      if (bits == 0)
+        break;
+      if (bits < 0)
+        return Result.KATE_E_BAD_PACKET;
+      opb.skip(bits);
+    }
+
+    return 0;
+  }
+
+  static private final int fp_bits = (4*8);
+  static private final int fp_cuts_bits = fp_bits/2-1;
+  static private final int fp_cuts_bits_bits = 4;
+  static private final int fp_sign_bit = 0x80000000;
+  private static int[] readFixed(Buffer opb, int count) {
+    int head = opb.read(fp_cuts_bits_bits);
+    int tail = opb.read(fp_cuts_bits_bits);
+    int bits = fp_bits - head - tail;
+    int values[] = new int[count];
+    int n = 0;
+    while (count-- > 0) {
+      int sign = 0;
+      if (head > 0)
+        sign = opb.read1();
+      int v = opb.read(bits);
+      v <<= tail;
+      if (sign != 0)
+        v = -v;
+      values[n++] = v;
+    }
+    return values;
+  }
+  static private double fixedToFloat(int v) {
+    return ((double)v) / (1<<16);
+  }
+
+  /**
+   * Read an array of float channels.
+   * Float format defined in the Kate specification.
+   */
+  static double[][] readFloats(Buffer opb, int count, int streams) {
+    if (count*streams == 0)
+     return null;
+    if (streams > 1) {
+      if (opb.read1() != 0) {
+        count *= streams;
+        streams = 1;
+      }
+    }
+
+    double values[][] = new double[streams][];
+    for (int s=0; s<streams; ++s) {
+      int ints[] = readFixed(opb, count);
+      values[s] = new double[count];
+      for (int c=0; c<count; ++c) {
+        values[s][c] = fixedToFloat(ints[c]);
+      }
+    }
+    return values;
+  }
+}

Added: trunk/cronus/com/fluendo/jkate/Buffer.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/Buffer.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/Buffer.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,177 @@
+/* Castor, a fast Vorbis decoder created by Michael Scheerer, adapted and
+ * integrated into a stripped fork of the jorbis framework.
+ *
+ * Castor decoder (c) 2009 Michael Scheerer www.meviatronic.com
+ * Jorbis framework (c) 2000 ymnk, JCraft,Inc. <ymnk at jcraft.com>
+ *
+ * Many thanks to
+ *   Monty <monty at xiph.org> and
+ *   The XIPHOPHORUS Company http://www.xiph.org/ .
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+   
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+import java.io.*;
+
+/**
+ * Bitstream processor for little endian style bitstream processing.
+ * Rewritten from:
+ *
+ * @author    Michael Scheerer
+ */
+public class Buffer{
+  	private static final int BUFFER_INCREMENT=256;
+	
+	protected final static int BYTELENGTH = 8;
+	protected final static int DOUBLE_BYTELENGTH = 16;
+	protected final static int TRIPLE_BYTELENGTH = 24;
+	protected final static int QUADRUPEL_BYTELENGTH = 32;
+
+	protected final static int BITMASK[] = {
+		0x00000000,
+		0x00000001, 0x00000003, 0x00000007, 0x0000000F,
+		0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF,
+		0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF,
+		0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF,
+		0x0001FFFF, 0x0003FFFF, 0x0007FFFF, 0x000FFFFF,
+		0x001FFFFF, 0x003FFFFF, 0x007FFFFF, 0x00FFFFFF,
+		0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF,
+		0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF
+	};
+
+	private int byteIdx;
+	private byte[] data;
+	private int revBitIdx;
+	private int packetByteIdx;
+	private int packetSize;
+	
+  	public void read(byte[] s, int bytes) {
+    	int i=0;
+    	while(bytes--!=0){
+      		s[i++]=(byte)(read(8));
+    	}
+  	}
+	
+  	public void loadPacket(byte[] buf, int bytes) {
+    	loadPacket(buf, 0, bytes);
+	}
+	
+	public void loadPacket(byte[] buf, int start, int bytes){
+    	byteIdx = start;
+    	data = buf;
+    	revBitIdx = packetByteIdx = 0;
+    	packetSize = bytes;
+	}
+	
+	public final int look1() {
+		
+	  	if (handleBounderies(1) < 0) {
+		  	return -1;
+		}
+
+		int val = data[byteIdx] >>> revBitIdx & 1;
+
+		return val;
+	}
+
+	public final int read1() {
+		
+	  	if (handleBounderies(1) < 0) {
+		  	return -1;
+		}
+
+		int val = data[byteIdx] >>> revBitIdx & 1;
+
+		revBitIdx++;
+		if (revBitIdx == BYTELENGTH) {
+			revBitIdx = 0;
+			byteIdx++;
+			packetByteIdx++;
+			
+		}
+
+		return val;
+	}
+	
+	public final int read(int i) {
+		if (i <= 0) {
+			return 0;
+		}
+		if (handleBounderies(i + revBitIdx) < 0) {
+			return -1;
+		}
+
+		int store = revBitIdx;
+		
+		int val = (data[byteIdx] & 0xFF) >>> store;
+		
+		revBitIdx += i;
+		
+		if (revBitIdx >= BYTELENGTH) {
+			byteIdx++;
+			packetByteIdx++;
+			val |= (data[byteIdx] & 0xFF) << BYTELENGTH - store;
+			if (revBitIdx >= DOUBLE_BYTELENGTH) {
+				byteIdx++;
+				packetByteIdx++;
+				val |= (data[byteIdx] & 0xFF) << DOUBLE_BYTELENGTH - store;
+				if (revBitIdx >= TRIPLE_BYTELENGTH) {
+					byteIdx++;
+					packetByteIdx++;
+					val |= (data[byteIdx] & 0xFF) << TRIPLE_BYTELENGTH - store;
+					if (revBitIdx >= QUADRUPEL_BYTELENGTH) {
+						byteIdx++;
+						packetByteIdx++;
+						val |= ((data[byteIdx] & 0xFF) << QUADRUPEL_BYTELENGTH - store) & 0xFF000000;
+					}
+				}
+			}
+			revBitIdx &= 7;
+		}
+		return val & BITMASK[i];
+	}
+	
+	final int skip(int i) {
+		if (handleBounderies(i + revBitIdx) < 0) {
+			return -1;
+		}
+		revBitIdx += i;
+		while (revBitIdx >= BYTELENGTH) {
+			revBitIdx -= BYTELENGTH;
+			byteIdx++;
+			packetByteIdx++;
+		}
+		return 0;
+	}
+	
+	public int bits(){
+    	return byteIdx * 8 + revBitIdx;
+  	}
+
+  	private int handleBounderies(int bits)  {
+		int ret = 0;
+		if(packetByteIdx + bits / 8 >= packetSize){
+        	byteIdx += bits / 8;
+        	packetByteIdx += bits / 8;
+			revBitIdx = bits & 7;
+        	return -1;
+		}
+		return ret;
+  	}
+}
+
+

Added: trunk/cronus/com/fluendo/jkate/Color.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/Color.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/Color.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,29 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+public class Color
+{
+  public byte r;
+  public byte g;
+  public byte b;
+  public byte a;
+}

Added: trunk/cronus/com/fluendo/jkate/Comment.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/Comment.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/Comment.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,35 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+/**
+ * Vorbis comments are used in Kate streams too.
+ */
+public class Comment {
+  String[] user_comments;
+  String vendor;
+
+
+  public void clear() {
+    user_comments = null;
+    vendor = null;
+  }
+}

Added: trunk/cronus/com/fluendo/jkate/Curve.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/Curve.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/Curve.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,31 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+/**
+ * A curve definition, splines, segments, etc.
+ */
+public class Curve
+{
+  public KateCurveType type;
+  public int npts;
+  public double pts[][];
+}

Added: trunk/cronus/com/fluendo/jkate/Decode.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/Decode.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/Decode.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,238 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+import org.xiph.ogg.*;
+import com.fluendo.utils.*;
+
+public final class Decode {
+
+  Buffer   opb = new Buffer();
+  Info     info;
+
+  public Decode (Info i) {
+    info = i;
+  }
+
+  /**
+   * Decode a text data packet (packet type 0x00), and fills the passed event
+   * from the decoded data.
+   */
+  int decodeTextPacket(Event ev)
+  {
+    ev.ki = info;
+
+    ev.start = Bitwise.read64(opb);
+    ev.duration = Bitwise.read64(opb);
+    ev.backlink = Bitwise.read64(opb);
+
+    ev.start_time = granuleDuration(ev.start);
+    ev.end_time = ev.start_time+granuleDuration(ev.duration);
+
+    /* text */
+    int len = Bitwise.read32(opb);
+    ev.text = new byte[len];
+    Bitwise.readbuf(opb, ev.text, len);
+
+    /* event ID */
+    ev.id = -1;
+    if (opb.read1() != 0) {
+      ev.id = Bitwise.read32v(opb);
+    }
+
+    /* motions */
+    ev.motions = null;
+    if (opb.read1() != 0) {
+      int nmotions = Bitwise.read32v(opb);
+      if (nmotions < 0)
+        return -1;
+      ev.motions = new Motion[nmotions];
+      for (int n=0; n<nmotions; ++n) {
+        if (opb.read1() != 0) {
+          int idx = Bitwise.read32v(opb);
+          if (idx < 0 || idx >= info.motions.length)
+            return -1;
+          ev.motions[n] = info.motions[idx];
+        }
+        else {
+          try {
+            ev.motions[n] = info.unpackMotion(opb);
+          }
+          catch (KateException ke) {
+            return Result.KATE_E_BAD_PACKET;
+          }
+        }
+      }
+    }
+
+    /* overrides */
+    if (opb.read1() != 0) {
+      try {
+        if (opb.read1() != 0)
+          ev.text_encoding = KateTextEncoding.CreateTextEncoding(opb.read(8));
+        if (opb.read1() != 0)
+          ev.text_directionality = KateTextDirectionality.CreateTextDirectionality(opb.read(8));
+      }
+      catch (KateException ke) {
+        return Result.KATE_E_BAD_PACKET;
+      }
+
+      /* language override */
+      if (opb.read1() != 0) {
+        int nbytes = Bitwise.read32v(opb);
+        if (nbytes < 0)
+          return Result.KATE_E_BAD_PACKET;
+        if (nbytes > 0) {
+          ev.language=new byte[nbytes];
+          Bitwise.readbuf(opb, ev.language, nbytes);
+        }
+      }
+
+      /* region override */
+      if (opb.read1() != 0) {
+        int idx = Bitwise.read32v(opb);
+        if (idx < 0 || idx >= info.regions.length)
+          return Result.KATE_E_BAD_PACKET;
+        ev.kr = info.regions[idx];
+      }
+      if (opb.read1() != 0) {
+        try {
+          ev.kr = info.unpackRegion(opb);
+        }
+        catch (KateException ke) {
+          return Result.KATE_E_BAD_PACKET;
+        }
+      }
+
+      /* style override */
+      if (opb.read1() != 0) {
+        int idx = Bitwise.read32v(opb);
+        if (idx < 0 || idx >= info.styles.length)
+          return Result.KATE_E_BAD_PACKET;
+        ev.ks = info.styles[idx];
+      }
+      if (opb.read1() != 0) {
+        try {
+          ev.ks = info.unpackStyle(opb);
+        }
+        catch (KateException ke) {
+          return Result.KATE_E_BAD_PACKET;
+        }
+      }
+      if (opb.read1() != 0) {
+        int idx = Bitwise.read32v(opb);
+        if (idx < 0 || idx >= info.styles.length)
+          return Result.KATE_E_BAD_PACKET;
+        ev.ks2 = info.styles[idx];
+      }
+      if (opb.read1() != 0) {
+        try {
+          ev.ks2 = info.unpackStyle(opb);
+        }
+        catch (KateException ke) {
+          return Result.KATE_E_BAD_PACKET;
+        }
+      }
+
+      /* font mapping */
+      if (opb.read1() != 0) {
+        int idx = Bitwise.read32v(opb);
+        if (idx < 0 || idx >= info.font_mappings.length)
+          return Result.KATE_E_BAD_PACKET;
+        ev.font_mapping = info.font_mappings[idx];
+      }
+    }
+
+    /* new in 0.2: palettes/bitmaps/markup type */
+    if (((info.bitstream_version_major<<8) | info.bitstream_version_minor) >= 0x0002) {
+      Bitwise.read32v(opb);
+      if (opb.read1() != 0) {
+        if (opb.read1() != 0) {
+          int idx = Bitwise.read32v(opb);
+          if (idx < 0 || idx >= info.palettes.length)
+            return Result.KATE_E_BAD_PACKET;
+          ev.palette = info.palettes[idx];
+        }
+        if (opb.read1() != 0) {
+          try {
+            ev.palette = info.unpackPalette(opb);
+          }
+          catch (KateException e) {
+            return Result.KATE_E_BAD_PACKET;
+          }
+        }
+        if (opb.read1() != 0) {
+          int idx = Bitwise.read32v(opb);
+          if (idx < 0 || idx >= info.bitmaps.length)
+            return Result.KATE_E_BAD_PACKET;
+          ev.bitmap = info.bitmaps[idx];
+        }
+        if (opb.read1() != 0) {
+          try {
+            ev.bitmap = info.unpackBitmap(opb);
+          }
+          catch (KateException e) {
+            return Result.KATE_E_BAD_PACKET;
+          }
+        }
+        if (opb.read1() != 0) {
+          try {
+            ev.markup_type = KateMarkupType.CreateMarkupType(opb.read(8));
+          }
+          catch (KateException e) {
+            return Result.KATE_E_BAD_PACKET;
+          }
+        }
+      }
+    }
+
+// TODO: remainder
+    return 0;
+  }
+
+  /**
+   * Convert a granule to a time.
+   */
+  public double granuleTime(long granulepos)
+  {
+    if(granulepos>=0){
+      long base=granulepos>>info.granule_shift;
+      long offset=granulepos-(base<<info.granule_shift);
+
+      return (base+offset)*
+        ((double)info.gps_denominator/info.gps_numerator);
+    }
+    return(-1);
+  }
+
+  /**
+   * Convert a time in granule units to a duration.
+   */
+  public double granuleDuration(long granule)
+  {
+    if(granule>=0){
+      return (granule)*
+        ((double)info.gps_denominator/info.gps_numerator);
+    }
+    return(-1);
+  }
+
+}

Added: trunk/cronus/com/fluendo/jkate/Event.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/Event.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/Event.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,69 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+public class Event
+{
+  public Info ki = null;
+
+  public long start;
+  public long duration;
+  public long backlink;
+  public double start_time;
+  public double end_time;
+
+  public int id = -1;
+
+  public KateTextEncoding text_encoding;
+  public KateTextDirectionality text_directionality;
+  public KateMarkupType markup_type;
+  public byte[] language;
+
+  public Region kr = null;
+  public Style ks = null;
+  public Style ks2 = null;
+  public Motion motions[] = null;
+  public Palette palette = null;
+  public Bitmap bitmap = null;
+  public FontMapping font_mapping = null;
+
+  public byte text[];
+
+  /**
+   * Initialize an event to safe defaults.
+   */
+  public Event(Info ki) {
+    this.ki = ki;
+    id = -1;
+    kr = null;
+    ks = null;
+    ks2 = null;
+    motions = null;
+    palette = null;
+    bitmap = null;
+    text = null;
+    font_mapping = null;
+    text_encoding = ki.text_encoding;
+    text_directionality = ki.text_directionality;
+    markup_type = ki.markup_type;
+    language = null;
+  }
+}

Added: trunk/cronus/com/fluendo/jkate/FontMapping.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/FontMapping.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/FontMapping.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,29 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+/**
+ * A set of ranges defining a mapping from code points to bitmaps.
+ */
+class FontMapping
+{
+  FontRange ranges[];
+}

Added: trunk/cronus/com/fluendo/jkate/FontRange.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/FontRange.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/FontRange.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,32 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+/**
+ * A code point range mapping to a set of bitmaps.
+ */
+public class FontRange
+{
+  int first_code_point;
+  int last_code_point;
+  int first_bitmap;
+}
+

Added: trunk/cronus/com/fluendo/jkate/Info.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/Info.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/Info.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,757 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+import org.xiph.ogg.*;
+import com.fluendo.utils.*;
+
+public class Info {
+  public int bitstream_version_major = -1;
+  public int bitstream_version_minor = -1;
+  public KateTextEncoding text_encoding;
+  public KateTextDirectionality text_directionality;
+  public int num_headers = 0;
+  public int granule_shift;
+  public int gps_numerator;
+  public int gps_denominator;
+  public String language;
+  public String category;
+  public Region regions[];
+  public Style styles[];
+  public Curve curves[];
+  public Motion motions[];
+  public Palette palettes[];
+  public Bitmap bitmaps[];
+  public FontRange font_ranges[];
+  public FontMapping font_mappings[];
+
+  public KateMarkupType markup_type;
+  public int original_canvas_width;
+  public int original_canvas_height;
+
+  /* used to track which header we need to decode next */
+  private int probe = 0;
+
+  /**
+   * Read a packet canvas size (width or height).
+   * Format defined in the Kate specification.
+   */
+  private static int read_canvas_size(Buffer opb)
+  {
+    int value, base, shift;
+
+    value = opb.read(8) | (opb.read(8)<<8);
+    base = value>>4;
+    shift = value&15;
+    return base<<shift;
+  }
+
+  /**
+   * Decodes a Kate info header packet and fills the class data with
+   * what was found in it.
+   */
+  private int unpackInfo(Buffer opb){
+    int reserved, tmp;
+
+    bitstream_version_major = (byte)opb.read(8);
+    bitstream_version_minor = (byte)opb.read(8);
+    Debug.info("Kate bitstream v"+bitstream_version_major+"."+bitstream_version_minor);
+
+    if (bitstream_version_major > 0)
+      return Result.KATE_E_VERSION;
+
+    num_headers  = (int)opb.read(8);
+    if (num_headers < 1)
+      return Result.KATE_E_BAD_PACKET;
+    tmp = opb.read(8);
+    if (tmp != 0)
+      return Result.KATE_E_BAD_PACKET;
+    try {
+      text_encoding = KateTextEncoding.CreateTextEncoding(tmp);
+      text_directionality = KateTextDirectionality.CreateTextDirectionality(opb.read(8));
+    }
+    catch (KateException e) {
+      return Result.KATE_E_BAD_PACKET;
+    }
+    reserved = opb.read(8);
+    if (bitstream_version_major==0 && bitstream_version_minor<3) {
+      if (reserved != 0)
+        return Result.KATE_E_BAD_PACKET;
+    }
+    granule_shift = opb.read(8);
+
+    original_canvas_width = read_canvas_size(opb);
+    original_canvas_height = read_canvas_size(opb);
+    
+    reserved = Bitwise.read32(opb);
+    if (bitstream_version_major==0 && bitstream_version_minor<3) {
+      if (reserved != 0)
+        return Result.KATE_E_BAD_PACKET;
+    }
+
+    gps_numerator = Bitwise.read32(opb);
+    gps_denominator = Bitwise.read32(opb);
+
+    if (granule_shift >= 64)
+      return Result.KATE_E_BAD_PACKET;
+    if (gps_numerator == 0 || gps_denominator == 0)
+      return Result.KATE_E_BAD_PACKET;
+
+    byte[] buffer16=new byte[16];
+    int buffer_characters;
+
+    language = "";
+    buffer_characters = 0;
+    Bitwise.readbuf(opb, buffer16, 16);
+    if (buffer16[15] != 0)
+      return Result.KATE_E_BAD_PACKET;
+    for (buffer_characters=0; buffer_characters<16; ++buffer_characters) {
+      if (buffer16[buffer_characters] == 0) break;
+    }
+    try { language = new String(buffer16, 0, buffer_characters, "US-ASCII"); }
+    catch (java.io.UnsupportedEncodingException e) { language = ""; }
+
+    category = "";
+    buffer_characters = 0;
+    Bitwise.readbuf(opb, buffer16, 16);
+    if (buffer16[15] != 0)
+      return Result.KATE_E_BAD_PACKET;
+    for (buffer_characters=0; buffer_characters<16; ++buffer_characters) {
+      if (buffer16[buffer_characters] == 0) break;
+    }
+    try { category = new String(buffer16, 0, buffer_characters, "US-ASCII"); }
+    catch (java.io.UnsupportedEncodingException e) { category = ""; }
+
+    /* end of packet */
+    if (opb.read(1) != -1)
+      return (Result.KATE_E_BAD_PACKET);
+
+    return(0);
+  }
+
+  /**
+   * Checks that we have reached the end of the packet.
+   */
+  static int checkEOP(Buffer opb) {
+    int bits = 7 & (8 - (opb.bits()&7));
+    if (bits > 0) {
+      if (opb.read(bits) != 0)
+        return Result.KATE_E_BAD_PACKET;
+    }
+    if (opb.look1() != -1)
+      return (Result.KATE_E_BAD_PACKET);
+    return 0;
+  }
+
+  /**
+   * Decodes a Vorbis comment packet.
+   *  straight copy of the jheora one
+   */
+  private int unpackComment (Comment kc, Buffer opb)
+  {
+    int i;
+    int len;
+    byte[] tmp;
+    int comments;
+
+    len = Bitwise.read32(opb);
+    if(len<0)
+      return(Result.KATE_E_BAD_PACKET);
+
+    tmp=new byte[len];
+    Bitwise.readbuf(opb, tmp, len);
+    kc.vendor=new String(tmp);
+
+    comments = Bitwise.read32(opb);
+    if(comments<0) {
+      kc.clear();
+      return Result.KATE_E_BAD_PACKET;
+    }
+    kc.user_comments=new String[comments];
+    for(i=0;i<comments;i++){
+      len = Bitwise.read32(opb);
+      if(len<0) {
+        kc.clear();
+        return Result.KATE_E_BAD_PACKET;
+      }
+
+      tmp=new byte[len];
+      Bitwise.readbuf(opb,tmp,len);
+      kc.user_comments[i]=new String(tmp);
+    }
+    return 0;
+  }
+
+  /**
+   * Decode a single region.
+   */
+  public Region unpackRegion (Buffer opb) throws KateException
+  {
+    Region kr = new Region();
+
+    kr.metric = KateSpaceMetric.CreateSpaceMetric(opb.read(8));
+    kr.x = Bitwise.read32v(opb);
+    kr.y = Bitwise.read32v(opb);
+    kr.w = Bitwise.read32v(opb);
+    kr.h = Bitwise.read32v(opb);
+    kr.style = Bitwise.read32v(opb);
+
+    if (((bitstream_version_major<<8) | bitstream_version_minor) >= 0x0002) {
+      Bitwise.read32v(opb);
+      kr.clip = (opb.read1() != 0);
+    }
+    else {
+      kr.clip = false;
+    }
+
+    Bitwise.skipWarp(opb);
+
+    return kr;
+  }
+
+  /**
+   * Decode the regions header packet.
+   */
+  private int unpackRegions (Buffer opb)
+  {
+    int nregions = Bitwise.read32v(opb);
+    if (nregions < 0) return Result.KATE_E_BAD_PACKET;
+    regions = new Region[nregions];
+    for (int n=0; n<nregions; ++n) {
+      try {
+        regions[n] = unpackRegion(opb);
+      }
+      catch (KateException ke) {
+        regions = null;
+        return Result.KATE_E_BAD_PACKET;
+      }
+    }
+
+    Bitwise.skipWarp(opb);
+
+    /* end of packet */
+    return checkEOP(opb);
+  }
+
+  /**
+   * Decode a color.
+   */
+  private Color unpackColor (Buffer opb)
+  {
+    Color color = new Color();
+    color.r = (byte)opb.read(8);
+    color.g = (byte)opb.read(8);
+    color.b = (byte)opb.read(8);
+    color.a = (byte)opb.read(8);
+    return color;
+  }
+
+  /**
+   * Decode a single style.
+   */
+  public Style unpackStyle (Buffer opb) throws KateException
+  {
+    Style ks = new Style();
+
+    double floats[][] = Bitwise.readFloats(opb, 8, 1);
+    int idx = 0;
+    ks.halign = floats[0][idx++];
+    ks.valign = floats[0][idx++];
+    ks.font_width = floats[0][idx++];
+    ks.font_height = floats[0][idx++];
+    ks.left_margin = floats[0][idx++];
+    ks.top_margin = floats[0][idx++];
+    ks.right_margin = floats[0][idx++];
+    ks.bottom_margin = floats[0][idx++];
+    ks.text_color = unpackColor(opb);
+    ks.background_color = unpackColor(opb);
+    ks.draw_color = unpackColor(opb);
+    ks.font_metric = KateSpaceMetric.CreateSpaceMetric(opb.read(8));
+    ks.margin_metric = KateSpaceMetric.CreateSpaceMetric(opb.read(8));
+    ks.bold = opb.read1() != 0;
+    ks.italics = opb.read1() != 0;
+    ks.underline = opb.read1() != 0;
+    ks.strike = opb.read1() != 0;
+
+    if (((bitstream_version_major<<8) | bitstream_version_minor) >= 0x0002) {
+      Bitwise.read32v(opb);
+      ks.justify = opb.read1() != 0;
+      int len = Bitwise.read32v(opb);
+      if (len < 0)
+        throw new KateBadPacketException();
+      byte s[] = new byte[len];
+      Bitwise.readbuf(opb, s, len);
+      ks.font = new String(s);
+    }
+    else {
+      ks.justify = false;
+      ks.font = null;
+    }
+
+    if (((bitstream_version_major<<8) | bitstream_version_minor) >= 0x0004) {
+      Bitwise.read32v(opb);
+      ks.wrap_mode = KateWrapMode.CreateWrapMode(Bitwise.read32v(opb));
+    }
+    else {
+      ks.wrap_mode = KateWrapMode.kate_wrap_word;
+    }
+
+    Bitwise.skipWarp(opb);
+
+    return ks;
+  }
+
+  /**
+   * Decode the styles header packet.
+   */
+  private int unpackStyles (Buffer opb)
+  {
+    int nstyles = Bitwise.read32v(opb);
+    if (nstyles < 0) return Result.KATE_E_BAD_PACKET;
+    styles = new Style[nstyles];
+    for (int n=0; n<nstyles; ++n) {
+      try {
+        styles[n] = unpackStyle(opb);
+      }
+      catch (KateException ke) {
+        styles = null;
+        return Result.KATE_E_BAD_PACKET;
+      }
+    }
+
+    Bitwise.skipWarp(opb);
+
+    /* end of packet */
+    return checkEOP(opb);
+  }
+
+  /**
+   * Decode a single curve.
+   */
+  private Curve unpackCurve (Buffer opb) throws KateException
+  {
+    Curve kc = new Curve();
+
+    kc.type = KateCurveType.CreateCurveType(opb.read(8));
+    kc.npts = Bitwise.read32v(opb);
+    if (kc.npts < 0)
+      throw new KateBadPacketException();
+    Bitwise.skipWarp(opb);
+
+    kc.pts = Bitwise.readFloats(opb, kc.npts, 2);
+
+    return kc;
+  }
+
+  /**
+   * Decode the curves header packet.
+   */
+  private int unpackCurves (Buffer opb)
+  {
+    int ncurves = Bitwise.read32v(opb);
+    if (ncurves < 0) return Result.KATE_E_BAD_PACKET;
+    curves = new Curve[ncurves];
+    for (int n=0; n<ncurves; ++n) {
+      try {
+        curves[n] = unpackCurve(opb);
+      }
+      catch (KateException ke) {
+        curves = null;
+        return Result.KATE_E_BAD_PACKET;
+      }
+    }
+
+    Bitwise.skipWarp(opb);
+
+    /* end of packet */
+    return checkEOP(opb);
+  }
+
+  /**
+   * Decode a single motion.
+   */
+  public Motion unpackMotion (Buffer opb) throws KateException
+  {
+    Motion km = new Motion();
+
+    int ncurves = Bitwise.read32v(opb);
+    if (ncurves < 0)
+      throw new KateBadPacketException();
+    km.curves = new Curve[ncurves];
+    for (int n=0; n<ncurves; ++n) {
+      if (opb.read1() != 0) {
+        int idx = Bitwise.read32v(opb);
+        if (idx < 0 || idx >= this.curves.length)
+          throw new KateBadPacketException();
+        km.curves[n] = this.curves[idx];
+      }
+      else {
+        km.curves[n] = unpackCurve(opb);
+      }
+    }
+
+    double floats[][] = Bitwise.readFloats(opb, ncurves, 1);
+    km.durations = new double[ncurves];
+    for (int n=0; n<ncurves; ++n) {
+      km.durations[n] = floats[0][n];
+    }
+
+    km.x_mapping = KateMotionMapping.CreateMotionMapping(opb.read(8));
+    km.y_mapping = KateMotionMapping.CreateMotionMapping(opb.read(8));
+    km.semantics = KateMotionSemantics.CreateMotionSemantics(opb.read(8));
+    km.periodic = (opb.read1() != 0);
+
+    Bitwise.skipWarp(opb);
+
+    return km;
+  }
+
+  /**
+   * Decode the motions header packet.
+   */
+  private int unpackMotions (Buffer opb)
+  {
+    int nmotions = Bitwise.read32v(opb);
+    if (nmotions < 0) return Result.KATE_E_BAD_PACKET;
+    motions = new Motion[nmotions];
+    for (int n=0; n<nmotions; ++n) {
+      try {
+        motions[n] = unpackMotion(opb);
+      }
+      catch (KateException ke) {
+        motions = null;
+        return Result.KATE_E_BAD_PACKET;
+      }
+    }
+
+    Bitwise.skipWarp(opb);
+
+    /* end of packet */
+    return checkEOP(opb);
+  }
+
+  /**
+   * Decode a single palette.
+   */
+  public Palette unpackPalette (Buffer opb) throws KateException
+  {
+    Palette kp = new Palette();
+
+    int ncolors = opb.read(8)+1;
+    kp.colors = new Color[ncolors];
+    for (int n=0; n<ncolors; ++n) {
+      kp.colors[n] = unpackColor(opb);
+    }
+
+    Bitwise.skipWarp(opb);
+
+    return kp;
+  }
+
+  /**
+   * Decodes the palettes packet.
+   */
+  private int unpackPalettes (Buffer opb)
+  {
+    int npalettes = Bitwise.read32v(opb);
+    if (npalettes < 0) return Result.KATE_E_BAD_PACKET;
+    palettes = new Palette[npalettes];
+    for (int n=0; n<npalettes; ++n) {
+      try {
+        palettes[n] = unpackPalette(opb);
+      }
+      catch (KateException ke) {
+        palettes = null;
+        return Result.KATE_E_BAD_PACKET;
+      }
+    }
+
+    Bitwise.skipWarp(opb);
+
+    /* end of packet */
+    return checkEOP(opb);
+  }
+
+  /**
+   * Decode a single bitmap.
+   */
+  public Bitmap unpackBitmap (Buffer opb) throws KateException
+  {
+    Bitmap kb = new Bitmap();
+
+    kb.width = Bitwise.read32v(opb);
+    kb.height = Bitwise.read32v(opb);
+    kb.bpp = opb.read(8);
+    if (kb.width < 0 || kb.height < 0 || kb.bpp < 0 || kb.bpp > 8)
+      throw new KateBadPacketException();
+
+    if (kb.bpp == 0) {
+      kb.type = KateBitmapType.CreateBitmapType(opb.read(8));
+      kb.palette = -1;
+      if (kb.type == KateBitmapType.kate_bitmap_type_paletted) {
+        int encoding = opb.read(8);
+        switch (encoding) {
+          case 1: /* RLE */
+            kb.bpp = Bitwise.read32v(opb);
+            kb.palette = Bitwise.read32v(opb);
+            kb.pixels = RLE.decodeRLE(opb, kb.width, kb.height, kb.bpp);
+            break;
+          default:
+            Debug.warning("Unsupported bitmap type");
+            throw new KateBadPacketException();
+        }
+      }
+      else if (kb.type == KateBitmapType.kate_bitmap_type_png) {
+        kb.size = Bitwise.read32(opb);
+        kb.pixels = new byte[kb.size];
+        Bitwise.readbuf(opb, kb.pixels, kb.size);
+      }
+      else {
+        Debug.warning("Unsupported bitmap type");
+        throw new KateBadPacketException();
+      }
+    }
+    else {
+      kb.type = KateBitmapType.kate_bitmap_type_paletted;
+      kb.palette = Bitwise.read32v(opb);
+      int npixels = kb.width * kb.height;
+      kb.pixels = new byte[npixels];
+      for (int n=0; n<npixels; ++n)
+        kb.pixels[n] = (byte)opb.read(kb.bpp);
+    }
+
+    if (((bitstream_version_major<<8) | bitstream_version_minor) >= 0x0004) {
+      Bitwise.read32v(opb);
+      kb.x_offset = Bitwise.read32v(opb);
+      kb.y_offset = Bitwise.read32v(opb);
+    }
+    else {
+      kb.x_offset = 0;
+      kb.y_offset = 0;
+    }
+
+    Bitwise.skipWarp(opb);
+
+    return kb;
+  }
+
+  /**
+   * Decodes the bitmaps packet.
+   */
+  private int unpackBitmaps (Buffer opb)
+  {
+    int nbitmaps = Bitwise.read32v(opb);
+    if (nbitmaps < 0) return Result.KATE_E_BAD_PACKET;
+    bitmaps = new Bitmap[nbitmaps];
+    for (int n=0; n<nbitmaps; ++n) {
+      try {
+        bitmaps[n] = unpackBitmap(opb);
+      }
+      catch (KateException ke) {
+        bitmaps = null;
+        return Result.KATE_E_BAD_PACKET;
+      }
+    }
+
+    Bitwise.skipWarp(opb);
+
+    /* end of packet */
+    return checkEOP(opb);
+  }
+
+  /**
+   * Decodes a single font range.
+   */
+  private FontRange unpackFontRange (Buffer opb)
+  {
+    FontRange fr = new FontRange();
+    fr.first_code_point = Bitwise.read32v(opb);
+    fr.last_code_point = Bitwise.read32v(opb);
+    fr.first_bitmap = Bitwise.read32v(opb);
+    Bitwise.skipWarp(opb);
+    return fr;
+  }
+
+  /**
+   * Decodes the font ranges/mappings packet.
+   */
+  private int unpackFontMappings (Buffer opb)
+  {
+    int nranges = Bitwise.read32v(opb);
+    if (nranges < 0)
+      return Result.KATE_E_BAD_PACKET;
+    if (nranges > 0) {
+      font_ranges = new FontRange[nranges];
+      for (int n=0; n<nranges; ++n) {
+        font_ranges[n] = unpackFontRange(opb);
+      }
+    }
+
+    int nmappings = Bitwise.read32v(opb);
+    if (nmappings < 0)
+      return Result.KATE_E_BAD_PACKET;
+    if (nmappings > 0) {
+      font_mappings = new FontMapping[nmappings];
+      for (int n=0; n<nmappings; ++n) {
+        nranges = Bitwise.read32v(opb);
+        if (nranges < 0)
+          return Result.KATE_E_BAD_PACKET;
+        if (nranges > 0) {
+          FontRange fr[] = new FontRange[nranges];
+          for (int l=0; l<nranges; ++l) {
+            if (opb.read1() != 0) {
+              int idx = Bitwise.read32v(opb);
+              if (idx < 0 || idx >= this.font_ranges.length)
+                return Result.KATE_E_BAD_PACKET;
+              fr[l] = this.font_ranges[idx];
+            }
+            else {
+              fr[l] = unpackFontRange(opb);
+            }
+          }
+          font_mappings[n].ranges = fr;
+        }
+        else {
+          font_mappings[n] = null;
+        }
+      }
+    }
+
+    Bitwise.skipWarp(opb);
+
+    /* end of packet */
+    return checkEOP(opb);
+  }
+
+  /**
+   * Resets the header decoder to the start, so a new stream may be decoded.
+   */
+  public void clear() {
+    num_headers = 0;
+    regions = null;
+    styles = null;
+    curves = null;
+    motions = null;
+    probe = 0;
+  }
+
+  /**
+   * Decodes a Kate header, updating the info with the data decoded from the header.
+   * If the next expected header is decoded properly, the next header will be expected.
+   * Headers beyond the ones we know about are ignored.
+   */
+  public int decodeHeader (Comment kc, Packet op)
+  {
+    long ret;
+    Buffer opb = new Buffer();
+
+    opb.loadPacket(op.packetBase, op.packet, op.bytes);
+  
+    {
+      byte[] id = new byte[7];
+      int typeflag;
+    
+      typeflag = opb.read(8);
+
+      /* header types have the MSB set */
+      if((typeflag & 0x80) == 0) {
+        return Result.KATE_E_BAD_PACKET;
+      }
+
+      /* Kate header magic */
+      Bitwise.readbuf(opb,id,7);
+      if (!"kate\0\0\0".equals(new String(id))) {
+        return Result.KATE_E_NOT_KATE;
+      }
+
+      if (op.packetno < num_headers) {
+        if (probe != op.packetno) return Result.KATE_E_BAD_PACKET;
+      }
+
+      /* reserved 0 byte */
+      if (opb.read(8) != 0)
+        return Result.KATE_E_BAD_PACKET;
+
+      Debug.debug("decodeHeader: packet type "+typeflag+", probe "+probe);
+
+      /* ensure packets are received in order */
+      switch(probe){
+      case 0:
+        if(op.bos == 0){
+          /* Not the initial packet */
+          return Result.KATE_E_BAD_PACKET;
+        }
+        ret = unpackInfo(opb);
+        break;
+
+      case 1:
+        ret = unpackComment(kc,opb);
+        break;
+    
+      case 2:
+        ret = unpackRegions(opb);
+        break;
+
+      case 3:
+        ret = unpackStyles(opb);
+        break;
+
+      case 4:
+        ret = unpackCurves(opb);
+        break;
+
+      case 5:
+        ret = unpackMotions(opb);
+        break;
+
+      case 6:
+        ret = unpackPalettes(opb);
+        break;
+
+      case 7:
+        ret = unpackBitmaps(opb);
+        break;
+
+      case 8:
+        ret = unpackFontMappings(opb);
+        /* last known header, we can init for decode */
+        if (ret == 0) {
+          Debug.debug("Found last known header, returning 1");
+          ret = 1;
+        }
+        break;
+
+      default:
+        /* ignore any trailing header packets for forward compatibility */
+        ret = 0;
+        break;
+      }
+    }
+
+    /* decode was successful, we're now expecting the next packet in sequence */
+    if (ret >= 0) {
+      ++probe;
+    }
+
+    return (int)ret;
+  }
+}
+

Added: trunk/cronus/com/fluendo/jkate/KateBadPacketException.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/KateBadPacketException.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/KateBadPacketException.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,26 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+class KateBadPacketException extends KateException {
+  public KateBadPacketException() { super("Bad packet"); }
+}
+

Added: trunk/cronus/com/fluendo/jkate/KateBitmapType.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/KateBitmapType.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/KateBitmapType.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,43 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+public class KateBitmapType {
+  public static final KateBitmapType kate_bitmap_type_paletted = new KateBitmapType ();
+  public static final KateBitmapType kate_bitmap_type_png = new KateBitmapType ();
+
+  private static final KateBitmapType[] list = {
+    kate_bitmap_type_paletted,
+    kate_bitmap_type_png,
+  };
+
+  private KateBitmapType() {
+  }
+
+  /**
+   * Create a KateBitmapType object from an integer.
+   */
+  public static KateBitmapType CreateBitmapType(int idx) throws KateException {
+    if (idx < 0 || idx >= list.length)
+      throw new KateException("Bitmap type "+idx+" out of bounds");
+    return list[idx];
+  }
+}

Added: trunk/cronus/com/fluendo/jkate/KateCurveType.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/KateCurveType.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/KateCurveType.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,51 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+public class KateCurveType {
+  public static final KateCurveType kate_curve_none = new KateCurveType ();
+  public static final KateCurveType kate_curve_static = new KateCurveType ();
+  public static final KateCurveType kate_curve_linear = new KateCurveType ();
+  public static final KateCurveType kate_curve_catmull_rom_spline = new KateCurveType ();
+  public static final KateCurveType kate_curve_bezier_cubic_spline = new KateCurveType ();
+  public static final KateCurveType kate_curve_bspline = new KateCurveType ();
+
+  private static final KateCurveType[] list = {
+    kate_curve_none,
+    kate_curve_static,
+    kate_curve_linear,
+    kate_curve_catmull_rom_spline,
+    kate_curve_bezier_cubic_spline,
+    kate_curve_bspline,
+  };
+
+  private KateCurveType() {
+  }
+
+  /**
+   * Create a KateCurveType object from an integer.
+   */
+  public static KateCurveType CreateCurveType(int idx) throws KateException {
+    if (idx < 0 || idx >= list.length)
+      throw new KateException("Curve type "+idx+" out of bounds");
+    return list[idx];
+  }
+}

Added: trunk/cronus/com/fluendo/jkate/KateException.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/KateException.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/KateException.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,26 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+class KateException extends java.lang.Exception {
+  public KateException(String s) { super(); }
+}
+

Added: trunk/cronus/com/fluendo/jkate/KateMarkupType.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/KateMarkupType.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/KateMarkupType.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,43 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+public class KateMarkupType {
+  public static final KateMarkupType kate_markup_none = new KateMarkupType ();
+  public static final KateMarkupType kate_markup_simple = new KateMarkupType ();
+
+  private static final KateMarkupType[] list = {
+    kate_markup_none,
+    kate_markup_simple,
+  };
+
+  private KateMarkupType() {
+  }
+
+  /**
+   * Create a KateMarkupType object from an integer.
+   */
+  public static KateMarkupType CreateMarkupType(int idx) throws KateException {
+    if (idx < 0 || idx >= list.length)
+      throw new KateException("Markup type "+idx+" out of bounds");
+    return list[idx];
+  }
+}

Added: trunk/cronus/com/fluendo/jkate/KateMotionMapping.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/KateMotionMapping.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/KateMotionMapping.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,51 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+public class KateMotionMapping {
+  public static final KateMotionMapping kmm_none = new KateMotionMapping ();
+  public static final KateMotionMapping kmm_frame = new KateMotionMapping ();
+  public static final KateMotionMapping kmm_window = new KateMotionMapping ();
+  public static final KateMotionMapping kmm_region = new KateMotionMapping ();
+  public static final KateMotionMapping kmm_event_duration = new KateMotionMapping ();
+  public static final KateMotionMapping kmm_bitmap_size = new KateMotionMapping ();
+
+  private static final KateMotionMapping[] list = {
+    kmm_none,
+    kmm_frame,
+    kmm_window,
+    kmm_region,
+    kmm_event_duration,
+    kmm_bitmap_size,
+  };
+
+  private KateMotionMapping() {
+  }
+
+  /**
+   * Create a KateMotionMapping object from an integer.
+   */
+  public static KateMotionMapping CreateMotionMapping(int idx) throws KateException {
+    if (idx < 0 || idx >= list.length)
+      throw new KateException("Motion mapping "+idx+" out of bounds");
+    return list[idx];
+  }
+}

Added: trunk/cronus/com/fluendo/jkate/KateMotionSemantics.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/KateMotionSemantics.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/KateMotionSemantics.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,119 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+public class KateMotionSemantics {
+  public static final KateMotionSemantics kms_time = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_z = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_region_position = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_region_size = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_text_alignment_int = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_text_alignment_ext = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_text_position = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_text_size = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_marker1_position = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_marker2_position = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_marker3_position = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_marker4_position = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_glyph_pointer_1 = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_glyph_pointer_2 = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_glyph_pointer_3 = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_glyph_pointer_4 = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_text_color_rg = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_text_color_ba = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_background_color_rg = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_background_color_ba = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_draw_color_rg = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_draw_color_ba = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_style_morph = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_text_path = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_text_path_section = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_draw = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_text_visible_section = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_horizontal_margins = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_vertical_margins = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_bitmap_position = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_bitmap_size = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_marker1_bitmap = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_marker2_bitmap = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_marker3_bitmap = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_marker4_bitmap = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_glyph_pointer_1_bitmap = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_glyph_pointer_2_bitmap = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_glyph_pointer_3_bitmap = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_glyph_pointer_4_bitmap = new KateMotionSemantics ();
+  public static final KateMotionSemantics kms_draw_width = new KateMotionSemantics ();
+
+  private static final KateMotionSemantics[] list = {
+    kms_time,
+    kms_z,
+    kms_region_position,
+    kms_region_size,
+    kms_text_alignment_int,
+    kms_text_alignment_ext,
+    kms_text_position,
+    kms_text_size,
+    kms_marker1_position,
+    kms_marker2_position,
+    kms_marker3_position,
+    kms_marker4_position,
+    kms_glyph_pointer_1,
+    kms_glyph_pointer_2,
+    kms_glyph_pointer_3,
+    kms_glyph_pointer_4,
+    kms_text_color_rg,
+    kms_text_color_ba,
+    kms_background_color_rg,
+    kms_background_color_ba,
+    kms_draw_color_rg,
+    kms_draw_color_ba,
+    kms_style_morph,
+    kms_text_path,
+    kms_text_path_section,
+    kms_draw,
+    kms_text_visible_section,
+    kms_horizontal_margins,
+    kms_vertical_margins,
+    kms_bitmap_position,
+    kms_bitmap_size,
+    kms_marker1_bitmap,
+    kms_marker2_bitmap,
+    kms_marker3_bitmap,
+    kms_marker4_bitmap,
+    kms_glyph_pointer_1_bitmap,
+    kms_glyph_pointer_2_bitmap,
+    kms_glyph_pointer_3_bitmap,
+    kms_glyph_pointer_4_bitmap,
+    kms_draw_width,
+  };
+
+  private KateMotionSemantics() {
+  }
+
+  /**
+   * Create a KateMotionSemantics object from an integer.
+   */
+  public static KateMotionSemantics CreateMotionSemantics(int idx) throws KateException {
+    if (idx < 0 || idx >= list.length)
+      throw new KateException("Motion semantics "+idx+" out of bounds");
+    return list[idx];
+  }
+}

Added: trunk/cronus/com/fluendo/jkate/KateSpaceMetric.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/KateSpaceMetric.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/KateSpaceMetric.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,45 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+public class KateSpaceMetric {
+  public static final KateSpaceMetric kate_metric_pixels = new KateSpaceMetric ();
+  public static final KateSpaceMetric kate_metric_percentage = new KateSpaceMetric ();
+  public static final KateSpaceMetric kate_metric_millionths = new KateSpaceMetric ();
+
+  private static final KateSpaceMetric[] list = {
+    kate_metric_pixels,
+    kate_metric_percentage,
+    kate_metric_millionths,
+  };
+
+  private KateSpaceMetric() {
+  }
+
+  /**
+   * Create a KateSpaceMetric object from an integer.
+   */
+  public static KateSpaceMetric CreateSpaceMetric(int idx) throws KateException {
+    if (idx < 0 || idx >= list.length)
+      throw new KateException("Space metrics "+idx+" out of bounds");
+    return list[idx];
+  }
+}

Added: trunk/cronus/com/fluendo/jkate/KateTextDirectionality.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/KateTextDirectionality.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/KateTextDirectionality.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,47 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+public class KateTextDirectionality {
+  public static final KateTextDirectionality kate_l2r_t2b = new KateTextDirectionality ();
+  public static final KateTextDirectionality kate_r2l_t2b = new KateTextDirectionality ();
+  public static final KateTextDirectionality kate_t2b_r2l = new KateTextDirectionality ();
+  public static final KateTextDirectionality kate_t2b_l2r = new KateTextDirectionality ();
+
+  private static final KateTextDirectionality[] list = {
+    kate_l2r_t2b,
+    kate_r2l_t2b,
+    kate_t2b_r2l,
+    kate_t2b_l2r
+  };
+
+  private KateTextDirectionality() {
+  }
+
+  /**
+   * Create a KateTextDirectionality object from an integer.
+   */
+  public static KateTextDirectionality CreateTextDirectionality(int idx) throws KateException {
+    if (idx < 0 || idx >= list.length)
+      throw new KateException("Text directionality "+idx+" out of bounds");
+    return list[idx];
+  }
+}

Added: trunk/cronus/com/fluendo/jkate/KateTextEncoding.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/KateTextEncoding.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/KateTextEncoding.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,41 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+public class KateTextEncoding {
+  public static final KateTextEncoding kate_utf8 = new KateTextEncoding ();
+
+  private static final KateTextEncoding[] list = {
+    kate_utf8,
+  };
+
+  private KateTextEncoding() {
+  }
+
+  /**
+   * Create a KateTextEncoding object from an integer.
+   */
+  public static KateTextEncoding CreateTextEncoding(int idx) throws KateException {
+    if (idx < 0 || idx >= list.length)
+      throw new KateException("Text encoding "+idx+" out of bounds");
+    return list[idx];
+  }
+}

Added: trunk/cronus/com/fluendo/jkate/KateWrapMode.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/KateWrapMode.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/KateWrapMode.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,43 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+public class KateWrapMode {
+  public static final KateWrapMode kate_wrap_word = new KateWrapMode ();
+  public static final KateWrapMode kate_wrap_none = new KateWrapMode ();
+
+  private static final KateWrapMode[] list = {
+    kate_wrap_word,
+    kate_wrap_none,
+  };
+
+  private KateWrapMode() {
+  }
+
+  /**
+   * Create a KateWrapMode object from an integer.
+   */
+  public static KateWrapMode CreateWrapMode(int idx) throws KateException {
+    if (idx < 0 || idx >= list.length)
+      throw new KateException("Wrap mode "+idx+" out of bounds");
+    return list[idx];
+  }
+}

Added: trunk/cronus/com/fluendo/jkate/Motion.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/Motion.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/Motion.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,35 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+/**
+ * A motion definition, composed of a series of curves
+ */
+public class Motion
+{
+  public Curve curves[];
+  public double durations[];
+  KateMotionMapping x_mapping;
+  KateMotionMapping y_mapping;
+  KateMotionSemantics semantics;
+  boolean periodic;
+}
+

Added: trunk/cronus/com/fluendo/jkate/Palette.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/Palette.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/Palette.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,29 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+/**
+ * A Palette definition, containing between 0 and 256 colors.
+ */
+public class Palette
+{
+  public Color colors[] = null;
+}

Added: trunk/cronus/com/fluendo/jkate/RLE.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/RLE.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/RLE.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,294 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+/**
+ * RLE decoding routines.
+ */
+public class RLE
+{
+  private static final int KATE_RLE_RUN_LENGTH_BITS_BASIC = 4;
+  private static final int KATE_RLE_RUN_LENGTH_BITS_BASIC_IN_DELTA = 3;
+  private static final int KATE_RLE_RUN_LENGTH_BITS_BASIC_STARTEND = 3;
+  private static final int KATE_RLE_RUN_LENGTH_BITS_BASIC_STOP = 6;
+  private static final int KATE_RLE_RUN_LENGTH_BITS_DELTA = 6;
+  private static final int KATE_RLE_RUN_LENGTH_BITS_BASIC_STARTEND_START = 9;
+  private static final int KATE_RLE_RUN_LENGTH_BITS_BASIC_STARTEND_END = 8;
+  private static final int KATE_RLE_RUN_LENGTH_BITS_BASIC_STOP_START = 8;
+  private static final int KATE_RLE_RUN_LENGTH_BITS_BASIC_IN_DELTA_STOP = 3;
+  private static final int KATE_RLE_RUN_LENGTH_BITS_DELTA_STOP = 5;
+  private static final int KATE_RLE_RUN_LENGTH_BITS_BASIC_ZERO = 8;
+  private static final int KATE_RLE_RUN_LENGTH_BITS_BASIC_NON_ZERO = 3;
+
+  private static final int KATE_RLE_TYPE_EMPTY = 0;
+  private static final int KATE_RLE_TYPE_BASIC = 1;
+  private static final int KATE_RLE_TYPE_DELTA = 2;
+  private static final int KATE_RLE_TYPE_BASIC_STOP = 3;
+  private static final int KATE_RLE_TYPE_BASIC_STARTEND = 4;
+  private static final int KATE_RLE_TYPE_DELTA_STOP = 5;
+  private static final int KATE_RLE_TYPE_BASIC_ZERO = 6;
+
+  private static final int KATE_RLE_TYPE_BITS = 3;
+
+  private static int decodeLineEmpty(Buffer opb, int width, byte pixels[], int offset, int bits, byte zero)
+  {
+    for (int n=0; n<width; ++n)
+      pixels[offset+n] = zero;
+    return 0;
+  }
+
+  private static int decodeLineBasic(Buffer opb, int width, byte pixels[], int offset, int bits, byte zero)
+  {
+    final int run_length_bits = KATE_RLE_RUN_LENGTH_BITS_BASIC;
+    int p = 0;
+    int count = width;
+    while (count > 0) {
+      int run_length = 1+opb.read(run_length_bits);
+      if (run_length == 0 || run_length > count)
+        return -1;
+      byte value = (byte)opb.read(bits);
+      for (int n=0; n<run_length; ++n)
+        pixels[offset+p++] = value;
+      count -= run_length;
+    }
+    return 0;
+  }
+
+  private static int decodeLineDelta(Buffer opb, int width, byte pixels[], int offset, int bits, byte zero)
+  {
+    final int run_length_delta_bits = KATE_RLE_RUN_LENGTH_BITS_DELTA;
+    final int run_length_basic_bits = KATE_RLE_RUN_LENGTH_BITS_BASIC_IN_DELTA;
+    int p = 0;
+    int count = width;
+    while (count > 0) {
+      int type = opb.read1();
+      if (type != 0) {
+        int run_length = 1+opb.read(run_length_delta_bits);
+        if (run_length == 0 || run_length > count)
+          return -1;
+        if (offset > 0) {
+          for (int n=0; n<run_length; ++n) {
+            pixels[offset+p] = pixels[offset+p-width];
+            ++p;
+          }
+        }
+        else {
+          for (int n=0; n<run_length; ++n)
+            pixels[offset+p++] = zero;
+        }
+        count -= run_length;
+      }
+      else {
+        int run_length = 1 + opb.read(run_length_basic_bits);
+        if (run_length == 0 || run_length > count)
+          return -1;
+        byte value = (byte)opb.read(bits);
+        for (int n=0; n<run_length; ++n)
+          pixels[offset+p++] = value;
+        count -= run_length;
+      }
+    }
+    return 0;
+  }
+
+  private static int decodeLineBasicStartEnd(Buffer opb, int width, byte pixels[], int offset, int bits, byte zero)
+  {
+    final int run_length_bits = KATE_RLE_RUN_LENGTH_BITS_BASIC_STARTEND;
+    int run_length;
+    int count = width;
+    int p = 0;
+
+    run_length = opb.read(KATE_RLE_RUN_LENGTH_BITS_BASIC_STARTEND_START);
+    if (run_length > 0) {
+      if (run_length > count)
+        return -1;
+      for (int n=0; n<run_length; ++n)
+        pixels[offset+p++] = zero;
+      count -= run_length;
+    }
+
+    run_length = opb.read(KATE_RLE_RUN_LENGTH_BITS_BASIC_STARTEND_END);
+    if (run_length > 0) {
+      if (run_length > count)
+        return -1;
+      for (int n=0; n<run_length; ++n)
+        pixels[offset+width-1-n] = zero;
+      count -= run_length;
+    }
+
+    while (count > 0) {
+      run_length = 1 + opb.read(run_length_bits);
+      if (run_length == 0 || run_length > count)
+        return -1;
+      byte value = (byte)opb.read(bits);
+      for (int n=0; n<run_length; ++n)
+        pixels[offset+p++] = value;
+      count -= run_length;
+    }
+
+    return 0;
+  }
+
+  private static int decodeLineBasicStop(Buffer opb, int width, byte pixels[], int offset, int bits, byte zero)
+  {
+    final int run_length_bits = KATE_RLE_RUN_LENGTH_BITS_BASIC_STOP;
+    int run_length;
+    int count = width;
+    int p = 0;
+
+    run_length = opb.read(KATE_RLE_RUN_LENGTH_BITS_BASIC_STOP_START);
+    if (run_length > 0) {
+      if (run_length > count)
+        return -1;
+      for (int n=0; n<run_length; ++n)
+        pixels[offset+p++] = zero;
+      count -= run_length;
+    }
+
+    while (count > 0) {
+      run_length = opb.read(run_length_bits);
+      if (run_length > count)
+        return -1;
+      if (run_length == 0) {
+        for (int n=0; n<run_length; ++n)
+          pixels[offset+p++] = zero;
+        break;
+      }
+      byte value = (byte)opb.read(bits);
+      for (int n=0; n<run_length; ++n)
+        pixels[offset+p++] = value;
+      count -= run_length;
+    }
+
+    return 0;
+  }
+
+  private static int decodeLineDeltaStop(Buffer opb, int width, byte pixels[], int offset, int bits, byte zero)
+  {
+    final int run_length_delta_bits = KATE_RLE_RUN_LENGTH_BITS_DELTA_STOP;
+    final int run_length_basic_bits = KATE_RLE_RUN_LENGTH_BITS_BASIC_IN_DELTA_STOP;
+    int run_length;
+    int count = width;
+    int p = 0;
+
+    while (count > 0) {
+      int type = opb.read1();
+      if (type != 0) {
+        run_length = 1 + opb.read(run_length_delta_bits);
+        if (run_length == 0 || run_length > count)
+          return -1;
+        if (offset > 0) {
+          for (int n=0; n<run_length; ++n) {
+            pixels[offset+p] = pixels[offset+p-width];
+            ++p;
+          }
+        }
+        else {
+          for (int n=0; n<run_length; ++n)
+            pixels[offset+p++] = zero;
+        }
+      }
+      else {
+        run_length = opb.read(run_length_basic_bits);
+        if (run_length == 0) {
+          for (int n=0; n<run_length; ++n)
+            pixels[offset+p++] = zero;
+          break;
+        }
+        if (run_length > count)
+          return -1;
+        byte value = (byte)opb.read(bits);
+        for (int n=0; n<run_length; ++n)
+          pixels[offset+p++] = value;
+      }
+      count -= run_length;
+    }
+
+    return 0;
+  }
+
+  private static int decodeLineBasicZero(Buffer opb, int width, byte pixels[], int offset, int bits, byte zero)
+  {
+    final int run_length_bits_zero = KATE_RLE_RUN_LENGTH_BITS_BASIC_ZERO;
+    final int run_length_bits_non_zero = KATE_RLE_RUN_LENGTH_BITS_BASIC_NON_ZERO;
+    int run_length;
+    int count = width;
+    int p = 0;
+
+    while (count > 0) {
+      byte value = (byte)opb.read(bits);
+      if (value == zero) {
+        run_length = 1 + opb.read(run_length_bits_zero);
+      }
+      else {
+        run_length = 1 + opb.read(run_length_bits_non_zero);
+      }
+      if (run_length == 0 || run_length > count)
+        return -1;
+      for (int n=0; n<run_length; ++n)
+        pixels[offset+p++] = value;
+      count -= run_length;
+    }
+
+    return 0;
+  }
+
+  public static byte[] decodeRLE(Buffer opb, int width, int height, int bpp)
+  {
+    byte[] pixels = new byte[width*height];
+    int offset = 0;
+    int ret;
+    byte zero = (byte)opb.read(bpp);
+    while (height > 0) {
+      int type = opb.read(KATE_RLE_TYPE_BITS);
+      switch (type) {
+        case KATE_RLE_TYPE_EMPTY:
+          ret = decodeLineEmpty(opb, width, pixels, offset, bpp, zero);
+          break;
+        case KATE_RLE_TYPE_DELTA:
+          ret = decodeLineDelta(opb, width, pixels, offset, bpp, zero);
+          break;
+        case KATE_RLE_TYPE_BASIC:
+          ret = decodeLineBasic(opb, width, pixels, offset, bpp, zero);
+          break;
+        case KATE_RLE_TYPE_BASIC_STARTEND:
+          ret = decodeLineBasicStartEnd(opb, width, pixels, offset, bpp, zero);
+          break;
+        case KATE_RLE_TYPE_BASIC_STOP:
+          ret = decodeLineBasicStop(opb, width, pixels, offset, bpp, zero);
+          break;
+        case KATE_RLE_TYPE_DELTA_STOP:
+          ret = decodeLineDeltaStop(opb, width, pixels, offset, bpp, zero);
+          break;
+        case KATE_RLE_TYPE_BASIC_ZERO:
+          ret = decodeLineBasicZero(opb, width, pixels, offset, bpp, zero);
+          break;
+        default:
+          return null;
+      }
+      if (ret != 0)
+        return null;
+      offset += width;
+      --height;
+    }
+    return pixels;
+  }
+}

Added: trunk/cronus/com/fluendo/jkate/Region.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/Region.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/Region.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,35 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+/**
+ * A region definition, for placement of text/images.
+ */
+public class Region
+{
+  public KateSpaceMetric metric;
+  public int x;
+  public int y;
+  public int w;
+  public int h;
+  public int style;
+  public boolean clip;
+}

Added: trunk/cronus/com/fluendo/jkate/Result.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/Result.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/Result.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,35 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+public final class Result {
+  public static final int KATE_E_NOT_FOUND            = -1;
+  public static final int KATE_E_INVALID_PARAMETER    = -2;
+  public static final int KATE_E_OUT_OF_MEMORY        = -3;
+  public static final int KATE_E_BAD_GRANULE          = -4;
+  public static final int KATE_E_INIT                 = -5;
+  public static final int KATE_E_BAD_PACKET           = -6;
+  public static final int KATE_E_TEXT                 = -7;
+  public static final int KATE_E_LIMIT                = -8;
+  public static final int KATE_E_VERSION              = -9;
+  public static final int KATE_E_NOT_KATE             = -10;
+  public static final int KATE_E_BAD_TAG              = -11;
+}

Added: trunk/cronus/com/fluendo/jkate/State.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/State.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/State.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,127 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+import org.xiph.ogg.*;
+import com.fluendo.utils.*;
+
+public class State
+{
+  long granulepos;
+
+  private Decode dec;
+  private Event ev = null;
+
+  public void clear()
+  {
+  }
+
+  /**
+   * Initialize for decoding.
+   */
+  public int decodeInit(Info ki)
+  {
+    dec = new Decode(ki);
+    granulepos=-1;
+
+    return(0);
+  }
+
+  /**
+   * Decode a Kate data packet.
+   * Headers are supposed to have been parsed already.
+   * An event may be generated, and will then be available from decodeEventOut.
+   */
+  public int decodePacketin (Packet op)
+  {
+    long ret;
+    byte type;
+
+    ev = null;
+
+    dec.opb.loadPacket(op.packetBase, op.packet, op.bytes);
+
+    /* get packet type */
+    type = (byte)dec.opb.read(8);
+
+    /* ignore headers */
+    if ((type & 0x80) != 0)
+      return 0;
+
+    switch (type) {
+      case 0x00:
+        ev = new Event(dec.info);
+        ret = dec.decodeTextPacket(ev);
+
+        if(ret < 0) {
+          ev = null;
+          return (int) ret;
+        }
+ 
+        if(op.granulepos>-1)
+          granulepos=op.granulepos;
+        else{
+          if(granulepos==-1){
+            granulepos=0;
+          }
+        }
+  
+        return 0;
+
+      case 0x01:
+        /* keepalive */
+        return 0;
+
+      case 0x7f:
+        /* eos */
+        return 1;
+
+      default:
+        Debug.debug("Kate packet type "+type+" ignored");
+        return 0;
+    }
+  }
+
+  /**
+   * Returns the event (if any) generated from the last decoded packet.
+   */
+  public Event decodeEventOut ()
+  {
+    return ev;
+  }
+
+  /**
+   * Returns, in seconds, absolute time of current packet in given
+   * logical stream
+   */
+  public double granuleTime(long granulepos)
+  {
+    return dec.granuleTime(granulepos);
+  }
+
+  /**
+   * Returns, in seconds, duration in granule units
+   */
+  public double granuleDuration(long granulepos)
+  {
+    return dec.granuleDuration(granulepos);
+  }
+}

Added: trunk/cronus/com/fluendo/jkate/Style.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/Style.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/Style.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,54 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+/**
+ * A style definition.
+ */
+
+public class Style
+{
+  public double halign;
+  public double valign;
+
+  public Color text_color;
+  public Color background_color;
+  public Color draw_color;
+
+  public KateSpaceMetric font_metric;
+  public double font_width;
+  public double font_height;
+
+  public KateSpaceMetric margin_metric;
+  public double left_margin;
+  public double top_margin;
+  public double right_margin;
+  public double bottom_margin;
+
+  public boolean bold;
+  public boolean italics;
+  public boolean underline;
+  public boolean strike;
+  public boolean justify;
+  public KateWrapMode wrap_mode;
+
+  public String font;
+}

Added: trunk/cronus/com/fluendo/jkate/Tracker.java
===================================================================
--- trunk/cronus/com/fluendo/jkate/Tracker.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jkate/Tracker.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,96 @@
+/* JKate
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JKate are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jkate;
+
+import java.util.*;
+import java.awt.*;
+import com.fluendo.utils.*;
+
+public final class Tracker {
+
+  private Dimension window;
+  private Dimension frame;
+
+  public com.fluendo.jkate.Event ev = null;
+
+  public boolean has[] = new boolean[64];
+  public static final int has_region = 0;
+  public static final int has_text_alignment_int = 1;
+  public static final int has_text_alignment_ext = 2;
+
+  public float region_x;
+  public float region_y;
+  public float region_w;
+  public float region_h;
+
+  public Tracker (com.fluendo.jkate.Event ev) {
+    this.ev = ev;
+  }
+
+  /**
+   * Update the tracker at the given time for the given image's dimensions.
+   */
+  public boolean update(double t, Dimension window, Dimension frame)
+  {
+    this.window = window;
+    this.frame = frame;
+
+    /* find current region and style, if any */
+    Region kr = ev.kr;
+    Style ks = ev.ks;
+    if (ks == null && kr != null && kr.style >= 0) {
+      ks = ev.ki.styles[kr.style];
+    }
+
+    /* start with nothing */
+    for (int n=0; n<has.length; ++n) has[n] = false;
+
+    /* define region */
+    if (kr != null) {
+      if (kr.metric == KateSpaceMetric.kate_metric_percentage) {
+        region_x = kr.x * frame.width / 100.0f;
+        region_y = kr.y * frame.height / 100.0f;
+        region_w = kr.w * frame.width / 100.0f;
+        region_h = kr.h * frame.height / 100.0f;
+      }
+      else if (kr.metric == KateSpaceMetric.kate_metric_millionths) {
+        region_x = kr.x * frame.width / 1000000.0f;
+        region_y = kr.y * frame.height / 1000000.0f;
+        region_w = kr.w * frame.width / 1000000.0f;
+        region_h = kr.h * frame.height / 1000000.0f;
+      }
+      else if (kr.metric == KateSpaceMetric.kate_metric_pixels) {
+        region_x = kr.x;
+        region_y = kr.y;
+        region_w = kr.w;
+        region_h = kr.h;
+      }
+      else {
+        Debug.debug("Invalid metrics");
+        return false;
+      }
+      has[has_region] = true;
+    }
+
+    return true;
+  }
+
+}

Added: trunk/cronus/com/fluendo/jst/Buffer.java
===================================================================
--- trunk/cronus/com/fluendo/jst/Buffer.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jst/Buffer.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,101 @@
+/* Cortado - a video player java applet
+ * Copyright (C) 2004 Fluendo S.L.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.jst;
+
+import java.util.*;
+
+public class Buffer {
+  
+  public static final int FLAG_DISCONT = (1<<0);
+  public static final int FLAG_DELTA_UNIT = (1<<1);
+
+  public int flags;
+
+  public java.lang.Object object;
+  public byte[] data;
+  public int offset;
+  public int length;
+  public Caps caps;
+
+  public long time_offset;
+  public long timestamp;
+  public long timestampEnd;
+
+  public static Buffer create() {
+		
+    Buffer result = new Buffer();
+      
+    result.time_offset = -1;
+    result.timestamp = -1;
+    result.timestampEnd = -1;
+    result.flags = 0;
+
+    return result;
+  }
+
+  public Buffer() {
+   
+  }
+
+  public boolean isFlagSet (int flag) {
+    return (flags & flag) == flag;
+  }
+  public void setFlag (int flag, boolean val) {
+    if (val)
+      flags |= flag;
+    else
+      flags &= ~flag;
+  }
+
+  public void close() {
+	  object = null;
+	  if (caps != null) {
+		  caps.close();
+	  }
+	  caps = null;
+	  data = null;
+  }
+
+  public void ensureSize (int length)
+  {
+    if (data == null) {
+      data = new byte[length];
+      //System.out.println("create data "+pool.size()+" "+live+" "+length);
+    }
+    else if (data.length < length) {
+      //System.out.println("expand buffer "+pool.size()+ " "+live+" "+data.length+" -> "+length);
+      data = new byte[length];
+    }
+  }
+
+  /**
+   * copies data into the buffer.
+   *
+   * @param data: the bytearray with data to copy
+   * @param offset: offset in the bytearray of first byte
+   * @param length: length of data to copy
+   */
+  public void copyData (byte[] data, int offset, int length)
+  {
+    ensureSize(length);
+    System.arraycopy (data, offset, this.data, 0, length);
+    this.offset = 0;
+    this.length = length;
+  }
+}

Added: trunk/cronus/com/fluendo/jst/Bus.java
===================================================================
--- trunk/cronus/com/fluendo/jst/Bus.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jst/Bus.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,112 @@
+/* Cortado - a video player java applet
+ * Copyright (C) 2004 Fluendo S.L.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.jst;
+
+import java.util.*;
+import com.fluendo.utils.*;
+
+public class Bus {
+  private Vector queue;
+  private Vector handlers;
+  private boolean flushing;
+  private BusSyncHandler syncHandler;
+
+  public Bus() {
+    queue = new Vector();
+    handlers = new Vector();
+    flushing = false;
+  }
+
+  public synchronized void addHandler (BusHandler handler) {
+    handlers.addElement (handler);
+  }
+  public synchronized void removeHandler (BusHandler handler) {
+    handlers.removeElement (handler);
+  }
+
+  public synchronized void setSyncHandler (BusSyncHandler handler) {
+    syncHandler = handler;
+  }
+
+  private void notifyHandlers (Vector handlers, Message message) {
+    Debug.debug("Bus.notifyHandlers: " + message);
+    for (Enumeration e = handlers.elements(); e.hasMoreElements();)
+    {
+      BusHandler handler = (BusHandler) e.nextElement();
+      Debug.debug("Notifying " + handler);
+      handler.handleMessage (message);
+    }
+  }
+
+  public void post(Message message) {
+    boolean post = true;
+    BusSyncHandler handler;
+
+    Debug.debug("Bus.post: " + message);
+
+    synchronized (this) {
+      if (flushing)
+	return;
+      handler = syncHandler;
+    }
+    post = (handler == null || handler.handleSyncMessage (message) == BusSyncHandler.PASS);
+
+    synchronized (this) {
+      if (post && !flushing) {
+        queue.addElement (message);
+        notifyAll();
+      }
+    }
+  }
+  public synchronized Message peek() {
+    if (queue.isEmpty() || flushing)
+      return null;
+    return (Message) queue.firstElement();
+  }
+  public synchronized Message pop() {
+    Message ret;
+
+    if (queue.isEmpty() || flushing)
+      return null;
+    ret = (Message) queue.elementAt(0);
+    queue.removeElementAt(0);
+    return ret;
+  }
+  public synchronized Message poll(long timeout) {
+    if (queue.isEmpty() && !flushing) {
+      try {
+        wait(timeout);
+      }
+      catch (InterruptedException e) {}
+    }
+    return pop();
+  }
+  public synchronized void setFlushing (boolean flush) {
+    flushing = flush;
+    queue.setSize(0);
+    notifyAll();
+  }
+  public void waitAndDispatch() {
+    Message msg;
+
+    msg = poll (0);
+    if (msg != null)
+      notifyHandlers (handlers, msg);
+  }
+}

Added: trunk/cronus/com/fluendo/jst/BusHandler.java
===================================================================
--- trunk/cronus/com/fluendo/jst/BusHandler.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jst/BusHandler.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,24 @@
+/* Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.jst;
+
+public interface BusHandler
+{
+  public void handleMessage (Message message);
+}

Added: trunk/cronus/com/fluendo/jst/BusSyncHandler.java
===================================================================
--- trunk/cronus/com/fluendo/jst/BusSyncHandler.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jst/BusSyncHandler.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,27 @@
+/* Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.jst;
+
+public interface BusSyncHandler
+{
+  public static final int DROP = 0;
+  public static final int PASS = 1;
+
+  public int handleSyncMessage (Message message);
+}

Added: trunk/cronus/com/fluendo/jst/Caps.java
===================================================================
--- trunk/cronus/com/fluendo/jst/Caps.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jst/Caps.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,103 @@
+/* Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.jst;
+
+import java.util.*;
+
+public class Caps
+{
+  protected String mime;
+  protected Hashtable fields = new Hashtable();
+
+  public synchronized String getMime () {
+    return mime;
+  }
+  public synchronized void setMime (String newMime) {
+    mime = newMime;
+  }
+
+  public Caps(String mime) {
+    super();
+    int sep1, sep2, sep3;
+    int len;
+    
+    len = mime.length();
+    sep1 = 0;
+    sep2 = mime.indexOf (';');
+    if (sep2 == -1)
+      sep2 = len;
+
+    this.mime = mime.substring(0, sep2);
+    while (sep2 < len) {
+      sep1 = sep2+1;
+      sep2 = mime.indexOf ('=', sep1);
+      sep3 = mime.indexOf (';', sep2);
+      if (sep3 == -1)
+        sep3 = len;
+      setField (mime.substring(sep1, sep2), mime.substring(sep2+1, sep3));
+      sep2 = sep3+1;
+    }
+  }
+
+  public String toString () {
+    StringBuffer buf = new StringBuffer();
+
+    buf.append("Caps: ");
+    buf.append(mime);
+    buf.append("\n");
+    for (Enumeration e = fields.keys(); e.hasMoreElements();) {
+      String key = (String) e.nextElement();
+      buf.append(" \"").append(key).append("\": \"").append(fields.get(key)).append("\"\n");
+    }
+    return buf.toString();
+  }
+
+  public void setField (String key, java.lang.Object value) {
+    fields.put (key, value);
+  }
+  public void setFieldInt (String key, int value) {
+    fields.put (key, new Integer (value));
+  }
+  public java.lang.Object getField (String key) {
+    return fields.get (key);
+  }
+  public int getFieldInt (String key, int def) {
+    Integer i;
+    i = (Integer) fields.get(key);
+    if (i == null)
+      return def;
+
+    return i.intValue();
+  }
+  public String getFieldString (String key, String def) {
+    String s = (String) fields.get(key);
+    if (s == null)
+      return def;
+
+    return s;
+  }
+	
+	public void close () {
+		if (fields != null) {
+			fields.clear();
+		}
+		fields = null;
+		mime = null;
+	}
+}

Added: trunk/cronus/com/fluendo/jst/CapsListener.java
===================================================================
--- trunk/cronus/com/fluendo/jst/CapsListener.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jst/CapsListener.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,24 @@
+/* Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.jst;
+
+public interface CapsListener
+{
+  public void capsChanged (Caps caps);
+}

Added: trunk/cronus/com/fluendo/jst/Clock.java
===================================================================
--- trunk/cronus/com/fluendo/jst/Clock.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jst/Clock.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,112 @@
+/* Cortado - a video player java applet
+ * Copyright (C) 2004 Fluendo S.L.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.jst;
+
+public abstract class Clock {
+  private long adjust;
+  private long lastTime;
+
+  public static final long USECOND = 1;
+  public static final long MSECOND = 1000 * USECOND;
+  public static final long SECOND = 1000 * MSECOND;
+
+  /* id types */
+  public static final int SINGLE = 0;
+  public static final int PERIODIC = 0;
+
+  public class ClockID {
+    long time;
+    long interval;
+    int type;
+    int status;
+    
+    public ClockID (long time, long interval, int type) {
+      this.time = time;
+      this.interval = interval;
+      this.type = type;
+    }
+
+    public long getTime() {
+      return time;
+    }
+
+    public WaitStatus waitID() {
+      WaitStatus res;
+      
+      res = waitFunc (this);
+
+      if (type == PERIODIC)
+        time += interval;
+
+      return res;
+    }
+    public void unschedule() {
+      unscheduleFunc(this);
+    }
+  }
+
+  public Clock()
+  {
+    adjust = 0;
+    lastTime = 0;
+  }
+
+  protected synchronized long adjust(long internal) {
+    long ret;
+
+    ret = internal + adjust;
+    /* make sure the time is increasing, else return last_time */
+    if (ret < lastTime) {
+      ret = lastTime;
+    } else {
+      lastTime = ret;
+    }
+    return ret;
+  }
+
+  protected abstract long getInternalTime();
+
+  protected abstract WaitStatus waitFunc(ClockID id);
+  protected abstract WaitStatus waitAsyncFunc(ClockID id);
+  protected abstract void unscheduleFunc(ClockID id);
+
+  public synchronized long getTime() {
+    long internal, ret;
+
+    internal = getInternalTime();
+    ret = adjust (internal);
+
+    return ret;
+  }
+  public synchronized void setAdjust(long newAdjust) {
+    adjust = newAdjust;
+  }
+  public synchronized long getAdjust() {
+    return adjust;
+  }
+
+  public ClockID newSingleShotID(long time) {
+    return new ClockID (time, 0, SINGLE);
+  }
+  public ClockID newPeriodicID(long time, long interval) {
+    return new ClockID (time, interval, PERIODIC);
+  }
+
+}
+

Added: trunk/cronus/com/fluendo/jst/ClockProvider.java
===================================================================
--- trunk/cronus/com/fluendo/jst/ClockProvider.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jst/ClockProvider.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,24 @@
+/* Cortado - a video player java applet
+ * Copyright (C) 2004 Fluendo S.L.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.jst;
+
+public interface ClockProvider {
+  public Clock provideClock();
+}
+

Added: trunk/cronus/com/fluendo/jst/Element.java
===================================================================
--- trunk/cronus/com/fluendo/jst/Element.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jst/Element.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,471 @@
+/* Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.jst;
+
+import java.util.*;
+import com.fluendo.utils.*;
+
+public abstract class Element extends com.fluendo.jst.Object
+{
+  public static final int FLAG_IS_SINK         = (com.fluendo.jst.Object.OBJECT_FLAG_LAST << 1);
+  public static final int ELEMENT_FLAG_LAST    = (com.fluendo.jst.Object.OBJECT_FLAG_LAST << 16);
+
+  protected Vector pads = new Vector();
+  protected java.lang.Object stateLock =  new java.lang.Object();
+  private Vector padListeners = new Vector();
+
+  protected Clock clock;
+  protected Bus bus;
+  protected long baseTime;
+
+  /* states */
+  public static final int NONE = 0;
+  public static final int STOP = 1;
+  public static final int PAUSE = 2;
+  public static final int PLAY = 3;
+
+  protected static String stateNames[] = {
+    "none",
+    "stop",
+    "pause",
+    "play"
+  };
+
+  private static final int SHIFT = 4;
+  private static final int MASK = 0xf;
+  /* transition */
+  public static final int STOP_PAUSE = (STOP << SHIFT) | PAUSE;
+  public static final int PAUSE_PLAY = (PAUSE << SHIFT) | PLAY;
+  public static final int PLAY_PAUSE = (PLAY << SHIFT) | PAUSE;
+  public static final int PAUSE_STOP = (PAUSE << SHIFT) | STOP;
+
+  /* state return values */
+  public static final int FAILURE = 0;
+  public static final int SUCCESS = 1;
+  public static final int ASYNC = 2;
+  public static final int NO_PREROLL = 3;
+
+  public static String stateReturnNames[] = {
+    "FAILURE",
+    "SUCCESS",
+    "ASYNC",
+    "NO_PREROLL"
+  };
+
+  /* current state, next and pending state */
+  protected int currentState;
+  protected int nextState;
+  protected int pendingState;
+  protected int lastReturn;
+
+  public static String getStateName(int state) {
+    return stateNames[state];
+  }
+
+  public static String getStateReturnName(int ret) {
+    return stateReturnNames[ret];
+  }
+
+  public String getMime ()
+  {
+    return null;
+  }
+  public abstract String getFactoryName ();
+
+  public int typeFind (byte[] data, int offset, int length)
+  {
+    return -1;
+  }
+
+  public Element() {
+    this(null);
+  }
+  public Element(String name) {
+    super (name);
+    currentState = STOP;
+    nextState = NONE;
+    pendingState = NONE;
+    lastReturn = SUCCESS;
+  }
+
+  public String toString ()
+  {
+    return "Element: ["+getName()+"]";
+  }
+
+  public synchronized void setClock (Clock newClock) {
+    Debug.debug(this + ".setClock(" + newClock + ")");
+    clock = newClock;
+  }
+  public synchronized Clock getClock () {
+    return clock;
+  }
+
+  public synchronized void setBus (Bus newBus) {
+    bus = newBus;
+  }
+  public synchronized Bus getBus () {
+    return bus;
+  }
+
+  public synchronized void addPadListener(PadListener listener)
+  {
+    padListeners.addElement (listener);
+  }
+  public synchronized void removePadListener(PadListener listener)
+  {
+    padListeners.removeElement (listener);
+  }
+  private synchronized void doPadListeners(int method, Pad pad)
+  {
+    for (Enumeration e = padListeners.elements(); e.hasMoreElements();) {
+      PadListener listener = (PadListener) e.nextElement();
+
+      switch (method) {
+        case 0:
+	  listener.padAdded (pad);
+	  break;
+        case 1:
+	  listener.padRemoved (pad);
+	  break;
+        case 2:
+	  listener.noMorePads ();
+	  break;
+      }
+    }
+  }
+
+  public synchronized Pad getPad(String name) {
+    for (Enumeration e = pads.elements(); e.hasMoreElements();) {
+      Pad pad = (Pad) e.nextElement();
+      if (name.equals(pad.getName()))
+        return pad;
+    }
+    return null; 
+  }
+  public synchronized boolean addPad(Pad newPad) {
+    if (newPad.setParent (this) == false)
+      return false;
+
+    pads.addElement (newPad);
+    doPadListeners (0, newPad);
+
+    return true;
+  }
+  public synchronized boolean removePad(Pad aPad) {
+    if (aPad.getParent() != this)
+      return false;
+    aPad.unParent();
+    pads.removeElement (aPad);
+    doPadListeners (1, aPad);
+    return true;
+  }
+  public synchronized void noMorePads() {
+    doPadListeners (2, null);
+  }
+
+  public Enumeration enumPads() {
+    return pads.elements();
+  }
+
+  public void postMessage (Message message) {
+    Bus myBus;
+
+    synchronized (this) {
+      myBus = bus;
+    }
+
+    if (myBus != null)
+      myBus.post (message);
+  }
+
+  public synchronized int getState(int[] resState, int[] resPending, long timeout) {
+    if (lastReturn == ASYNC) {
+      if (pendingState != NONE) {
+        long t;
+	
+	if (timeout == 0)
+	  t = 0;
+	else if (timeout < 1000)
+	  t = 1;
+	else 
+	  t = timeout / 1000;
+
+        try {
+          wait (t);
+	}
+	catch (InterruptedException e) {}
+      }
+    }
+
+    if (resState != null)
+      resState[0] = currentState;
+    if (resPending != null)
+      resPending[0] = pendingState;
+
+    return lastReturn;
+  }
+
+  private boolean padsActivate (boolean active)
+  {
+    int mode = (active ? Pad.MODE_PUSH : Pad.MODE_NONE);
+    boolean res = true;
+
+    for (Enumeration e = pads.elements(); e.hasMoreElements();) {
+      Pad pad = (Pad) e.nextElement();
+      res &= pad.activate(mode);
+      if (!res)
+        return res;
+    }
+    return res; 
+  }
+
+  public int getStateNext (int current, int pending)
+  {
+    int sign;
+
+    sign = pending - current;
+    if (sign > 0) sign = 1;
+    else if (sign < 0) sign = -1;
+    else sign = 0;
+
+    return current + sign;
+  }
+
+  public int getTransition (int current, int next)
+  {
+    return (current << SHIFT) | next;
+  }
+
+  public int getTransitionCurrent (int transition)
+  {
+    return transition >> SHIFT;
+  }
+
+  public int getTransitionNext (int transition)
+  {
+    return transition & MASK;
+  }
+
+  public int continueState(int result)
+  {
+    int oldRet, oldState, oldNext;
+    int current, next, pending;
+    Message message = null;
+    int transition = 0;
+
+    synchronized (this) {
+
+      oldRet = lastReturn;
+      lastReturn = result;
+      pending = pendingState;
+
+      if (pending == NONE)
+        return result;
+    
+      oldState = currentState;
+      oldNext = nextState;
+      current = currentState = oldNext;
+
+      if (pending == current) {
+        pendingState = NONE;
+        nextState = NONE;
+	transition = 0;
+
+	if (oldState != oldNext || oldRet == ASYNC) {
+          message = Message.newStateChanged (this,
+		            oldState, oldNext, pending);
+        }
+      }
+      else {
+        next = getStateNext (current, pending); 
+        transition = getTransition (current, next);
+
+	nextState = next;
+        lastReturn = ASYNC;
+
+        message = Message.newStateChanged (this,
+	            oldState, oldNext, pending);
+      }
+    }
+
+    if (message != null)
+      postMessage (message);
+
+    if (transition != 0) {
+      result = doChangeState (transition);
+    }
+    else {
+      synchronized (this) {
+        notifyAll();
+      }
+    }
+
+    return result; 
+  }
+
+  public synchronized void abortState()
+  {
+    if (pendingState != NONE && lastReturn != FAILURE) {
+      lastReturn = FAILURE;
+      notifyAll();
+    }
+  }
+  public void lostState()
+  {
+    boolean post = false;
+    int current = 0;
+
+    synchronized (this) {
+      if (pendingState == NONE && lastReturn != FAILURE) {
+        current = currentState;
+        pendingState = nextState = currentState;
+	lastReturn = ASYNC;
+	post = true;
+      }
+    }
+    if (post) {
+      postMessage (Message.newStateChanged (this, current, current, current));
+      postMessage (Message.newStateDirty (this));
+    }
+  }
+
+  protected int changeState(int transition)
+  {
+    boolean res;
+    int current, next;
+
+    current = getTransitionCurrent (transition);
+    next = getTransitionNext (transition);
+
+    if (next == NONE || current == next)
+      return lastReturn;
+
+    switch (transition) {
+      case STOP_PAUSE:
+        res = padsActivate(true);
+        break;
+      case PAUSE_PLAY:
+        res = true;
+        break;
+      case PLAY_PAUSE:
+        res = true;
+        break;
+      case PAUSE_STOP:
+        res = padsActivate(false);
+        break;
+      default:
+        res = false;
+    }
+    if (res)
+      return SUCCESS;
+    else
+      return FAILURE;
+  }
+
+  private int doChangeState(int transition)
+  {
+    int result;
+    int current, next;
+
+    current = getTransitionCurrent (transition);
+    next = getTransitionNext (transition);
+
+    result = changeState (transition);
+
+    switch (result) {
+      case FAILURE:
+	abortState();
+	break;
+      case SUCCESS:
+      case NO_PREROLL:
+        result = continueState(result);
+        break;
+      case ASYNC:
+        if (current < next) {
+          synchronized (this) {
+	    if (pendingState != NONE) {
+              lastReturn = result;
+	    }
+          }
+	}
+	else {
+          result = continueState(SUCCESS);
+	}
+        break;
+    }
+    return result;
+  }
+
+  public final int setState(int newState)
+  {
+    int result;
+    int transition;
+    int oldPending;
+
+    synchronized (stateLock) {
+
+      synchronized (this) {
+        if (lastReturn == FAILURE) {
+	  nextState = NONE;
+	  pendingState = NONE;
+	  lastReturn = SUCCESS;
+	}
+
+	oldPending = pendingState;
+
+	pendingState = newState;
+
+        if (oldPending != NONE) {
+          /* upwards state change will happen ASYNC */
+          if (oldPending <= newState) {
+	    lastReturn = ASYNC;
+	    return ASYNC;
+	  }
+          /* element is going to this state already */
+          else if (nextState == newState) {
+	    lastReturn = ASYNC;
+	    return ASYNC;
+	  }
+          /* element was performing an ASYNC upward state change and
+           * we request to go downward again. Start from the next pending
+           * state then. */
+          else if (nextState > newState && lastReturn == ASYNC) {
+            currentState = nextState;
+          }
+        }
+        nextState = getStateNext (currentState, newState);
+        transition = getTransition (currentState, nextState);
+      }
+      result = doChangeState (transition);
+    }
+    return result;
+  }
+
+  public boolean sendEvent (Event event) {
+    return false;
+  }
+  public boolean query (Query query) {
+    return false;
+  }
+
+  public Pad requestSinkPad(Pad peer) {
+    return null;
+  }
+}

Added: trunk/cronus/com/fluendo/jst/ElementFactory.java
===================================================================
--- trunk/cronus/com/fluendo/jst/ElementFactory.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jst/ElementFactory.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,151 @@
+/* Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+package com.fluendo.jst;
+
+import com.fluendo.utils.Debug;
+import java.util.Enumeration;
+import java.util.Vector;
+
+
+public class ElementFactory {
+
+    private static String[] components = {
+        "com.fluendo.plugin.HTTPSrc",
+        "com.fluendo.plugin.VideoSink",
+        "com.fluendo.plugin.AudioSinkJ2",
+        "com.fluendo.plugin.AudioSinkSA",
+        "com.fluendo.plugin.Queue",
+        "com.fluendo.plugin.FakeSink",
+        "com.fluendo.plugin.Overlay",
+        "com.fluendo.plugin.Selector",
+        "com.fluendo.plugin.OggDemux",
+        "com.fluendo.plugin.TheoraDec",
+        "com.fluendo.plugin.VorbisDec",
+        "com.fluendo.plugin.KateDec",
+        "com.fluendo.plugin.KateOverlay"
+    };
+    private static Vector elements = new Vector();
+
+    static {
+        loadElements();
+    }
+
+    public static void loadElements() {
+        try {
+
+            for (int i = 0; i < components.length; ++i) {
+                String str = components[i];
+                try {
+                    Class cl = Class.forName(str);
+                    Debug.log(Debug.INFO, "registered plugin: " + str);
+                    Element pl = (Element) cl.newInstance();
+                    elements.addElement(pl);
+                } catch (Throwable t) {
+                    Debug.log(Debug.INFO, "Failed to register plugin: " + str);
+                }
+            }
+
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private static final Element dup(Element element, String name) {
+        Element result = null;
+
+        Class cl = element.getClass();
+        try {
+            result = (Element) cl.newInstance();
+            if (result != null && name != null) {
+                result.setName(name);
+            }
+            Debug.log(Debug.INFO, "create element: " + result);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return result;
+    }
+
+    private static final Element findTypeFind(byte[] data, int offset, int length) {
+        int best = -1;
+        Element result = null;
+
+        for (Enumeration e = elements.elements(); e.hasMoreElements();) {
+            Element element = (Element) e.nextElement();
+
+            int rank = element.typeFind(data, offset, length);
+            if (rank > best) {
+                best = rank;
+                result = element;
+            }
+        }
+        return result;
+    }
+
+    public static final String typeFindMime(byte[] data, int offset, int length) {
+        Element elem;
+        String result = null;
+
+        elem = findTypeFind(data, offset, length);
+        if (elem != null) {
+            result = elem.getMime();
+        }
+        return result;
+    }
+
+    public static final Element makeTypeFind(byte[] data, int offset, int length, String name) {
+        Element result = null;
+
+        result = findTypeFind(data, offset, length);
+
+        if (result != null) {
+            result = dup(result, name);
+        }
+        return result;
+    }
+
+    public static final Element makeByMime(String mime, String name) {
+        Element result = null;
+
+        for (Enumeration e = elements.elements(); e.hasMoreElements();) {
+            Element element = (Element) e.nextElement();
+
+            if (mime.equals(element.getMime())) {
+                result = dup(element, name);
+                break;
+            }
+        }
+        return result;
+    }
+
+    public static final Element makeByName(String name, String elemName) {
+        Element result = null;
+
+        for (Enumeration e = elements.elements(); e.hasMoreElements();) {
+            Element element = (Element) e.nextElement();
+
+            if (name.equals(element.getFactoryName())) {
+                result = dup(element, elemName);
+                break;
+            }
+        }
+        return result;
+    }
+}
+

Added: trunk/cronus/com/fluendo/jst/Event.java
===================================================================
--- trunk/cronus/com/fluendo/jst/Event.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jst/Event.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,117 @@
+/* Cortado - a video player java applet
+ * Copyright (C) 2004 Fluendo S.L.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.jst;
+
+public class Event {
+
+  /* types */
+  public static final int FLUSH_START = 1;
+  public static final int FLUSH_STOP = 2;
+  public static final int EOS = 3;
+  public static final int NEWSEGMENT = 4;
+  public static final int SEEK = 5;
+  
+  private static String typeNames[] = {
+    "NULL",
+    "FLUSH_START",
+    "FLUSH_STOP",
+    "EOS",
+    "NEWSEGMENT",
+    "SEEK"
+  };
+
+  private int type;
+  private int format;
+  private boolean update;
+  private long start;
+  private long stop;
+  private long position;
+
+  private Event(int type) {
+    position = -1;
+    this.type = type;
+  }
+
+  public String toString() {
+    String typeName = typeNames[type];
+    switch (type) {
+      case SEEK:
+	return "[Event] type: " + typeName + ", format: " + format + ", position: " + position;
+      case NEWSEGMENT:
+	return "[Event] type: " + typeName + ( update ? ", update" : ", non-update" ) +
+	  ", format: " + format + ", start: " + start + ", stop: " + stop + ", position: " + position;
+      default:
+	return "[Event] type: " + typeName;
+    }
+  }
+
+  public int getType () {
+    return type;
+  }
+
+  public static Event newEOS() {
+    return new Event(EOS);
+  }
+
+  public static Event newFlushStart() {
+    return new Event(FLUSH_START);
+  }
+  public static Event newFlushStop() {
+    return new Event(FLUSH_STOP);
+  }
+
+  public static Event newSeek(int format, long position)
+  {
+    Event e = new Event(SEEK);
+    e.format = format;
+    e.position = position;
+    return e;
+  }
+  public long parseSeekPosition () {
+    return position;
+  }
+  public int parseSeekFormat () {
+    return format;
+  }
+
+  public static Event newNewsegment(boolean update, int format, long start, long stop, long position) {
+    Event e = new Event(NEWSEGMENT);
+    e.update = update;
+    e.format = format;
+    e.start = start;
+    e.stop = stop;
+    e.position = position;
+    return e;
+  }
+  public boolean parseNewsegmentUpdate () {
+    return update;
+  }
+  public int parseNewsegmentFormat () {
+    return format;
+  }
+  public long parseNewsegmentStart () {
+    return start;
+  }
+  public long parseNewsegmentStop () {
+    return stop;
+  }
+  public long parseNewsegmentPosition () {
+    return position;
+  }
+}

Added: trunk/cronus/com/fluendo/jst/Format.java
===================================================================
--- trunk/cronus/com/fluendo/jst/Format.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jst/Format.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,31 @@
+/* Cortado - a video player java applet
+ * Copyright (C) 2004 Fluendo S.L.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.jst;
+
+public class Format {
+  public static final int UNKNOWN = 0;
+  public static final int DEFAULT = 1;
+  public static final int BYTES = 2;
+  public static final int TIME = 3;
+  public static final int BUFFERS = 4;
+  public static final int PERCENT = 5;
+
+  public static final long PERCENT_MAX = 1000000;
+  public static final long PERCENT_SCALE = 10000;
+}

Added: trunk/cronus/com/fluendo/jst/Message.java
===================================================================
--- trunk/cronus/com/fluendo/jst/Message.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jst/Message.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,195 @@
+/* Cortado - a video player java applet
+ * Copyright (C) 2004 Fluendo S.L.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.jst;
+
+public class Message {
+
+  /* types */
+  public static final int EOS               = (1 << 0);
+  public static final int ERROR             = (1 << 1);
+  public static final int WARNING           = (1 << 2);
+  public static final int INFO              = (1 << 3);
+  public static final int TAG               = (1 << 4);
+  public static final int BUFFERING         = (1 << 5);
+  public static final int STATE_CHANGED     = (1 << 6);
+  public static final int STATE_DIRTY       = (1 << 7);
+  public static final int STEP_DONE         = (1 << 8);
+  public static final int CLOCK_PROVIDE     = (1 << 9);
+  public static final int CLOCK_LOST        = (1 << 10);
+  public static final int NEW_CLOCK         = (1 << 11);
+  public static final int STRUCTURE_CHANGE  = (1 << 12);
+  public static final int STREAM_STATUS     = (1 << 13);
+  public static final int APPLICATION       = (1 << 14);
+  public static final int ELEMENT           = (1 << 15);
+  public static final int SEGMENT_START     = (1 << 16);
+  public static final int SEGMENT_DONE      = (1 << 17);
+  public static final int DURATION          = (1 << 18);
+  public static final int RESOURCE          = (1 << 19);
+  public static final int BYTEPOSITION      = (1 << 20);
+
+  private com.fluendo.jst.Object src;
+  private int type;
+
+  private boolean boolVal;
+  private int intVal;
+  private long longVal;
+  private String stringVal;
+  private int old, next, pending;
+
+  private Message(com.fluendo.jst.Object src, int type) {
+    this.src = src;
+    this.type = type;
+  }
+
+  public com.fluendo.jst.Object getSrc () {
+    return src;
+  }
+  public int getType () {
+    return type;
+  }
+  public String toString ()
+  {
+    switch (type) {
+      case EOS:
+        return "[Message]: "+src+" type: EOS";
+      case BUFFERING:
+        return "[Message]: "+src+" type: BUFFERING, busy:"+boolVal+", percent:"+intVal;
+      case STATE_CHANGED:
+        return "[Message]: "+src+" type: STATE_CHANGED, old: " + Element.getStateName(old) +
+	  ", next: " + Element.getStateName(next)+
+	  ", pending: " + Element.getStateName(pending);
+      case STATE_DIRTY:
+        return "[Message]: "+src+" type: STATE_DIRTY";
+      case STREAM_STATUS:
+        return "[Message]: "+src+" type: STREAM_STATUS, "+(boolVal?"start":"stop")+", reason: "+
+			Pad.getFlowName(intVal)+", "+stringVal;
+      case ERROR:
+        return "[Message]: "+src+" type: ERROR, "+stringVal;
+      default:
+        return "[Message]: "+src+" type: "+type;
+    }
+  }
+
+  public static Message newEOS(com.fluendo.jst.Object src) {
+    return new Message(src, EOS);
+  }
+  public static Message newError(com.fluendo.jst.Object src, String str) {
+    Message msg;
+
+    msg = new Message(src, ERROR);
+    msg.stringVal = str;
+
+    return msg;
+  }
+  public static Message newWarning(com.fluendo.jst.Object src, String str) {
+    Message msg;
+
+    msg = new Message(src, WARNING);
+    msg.stringVal = str;
+
+    return msg;
+  }
+  public String parseErrorString() {
+    return stringVal;
+  }
+
+  public static Message newBuffering(com.fluendo.jst.Object src, boolean busy, int percent) {
+    Message msg;
+    
+    msg = new Message(src, BUFFERING);
+    msg.boolVal = busy;
+    msg.intVal = percent;
+
+    return msg;
+  }
+  public boolean parseBufferingBusy() {
+    return boolVal;
+  }
+  public int parseBufferingPercent() {
+    return intVal;
+  }
+
+  public static Message newStateChanged(com.fluendo.jst.Object src, int old, int next, int pending) {
+    Message msg = new Message(src, STATE_CHANGED);
+    msg.old = old;
+    msg.next = next;
+    msg.pending = pending;
+    return msg;
+  }
+  public int parseStateChangedOld() {
+    return old;
+  }
+  public int parseStateChangedNext() {
+    return next;
+  }
+  public int parseStateChangedPending() {
+    return pending;
+  }
+
+  public static Message newStateDirty(com.fluendo.jst.Object src) {
+    return new Message(src, STATE_DIRTY);
+  }
+
+  public static Message newStreamStatus(com.fluendo.jst.Object src, boolean start, int reason, String aString) {
+    Message msg = new Message(src, STREAM_STATUS);
+    msg.stringVal = aString;
+    msg.boolVal = start;
+    msg.intVal = reason;
+    return msg;
+  }
+  public String parseStreamStatusString() {
+    return stringVal;
+  }
+  public boolean parseStreamStatusStart() {
+    return boolVal;
+  }
+  public int parseStreamStatusReason() {
+    return intVal;
+  }
+
+  public static Message newResource(com.fluendo.jst.Object src, String aString) {
+    Message msg = new Message(src, RESOURCE);
+    msg.stringVal = aString;
+    return msg;
+  }
+  public String parseResourceString() {
+    return stringVal;
+  }
+  public static Message newDuration(com.fluendo.jst.Object src, int aFmt, long aDur) {
+    Message msg = new Message(src, DURATION);
+    msg.intVal = aFmt;
+    msg.longVal = aDur;
+    return msg;
+  }
+  public int parseDurationFormat() {
+    return intVal;
+  }
+  public long parseDurationValue() {
+    return longVal;
+  }
+  public static Message newBytePosition(com.fluendo.jst.Object src, long aPos) {
+    Message msg = new Message(src, BYTEPOSITION);
+    msg.longVal = aPos;
+    return msg;
+  }
+  public long parseBytePosition() {
+      return longVal;
+  }
+
+}

Added: trunk/cronus/com/fluendo/jst/Object.java
===================================================================
--- trunk/cronus/com/fluendo/jst/Object.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jst/Object.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,79 @@
+/* Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.jst;
+
+public abstract class Object
+{
+  protected String name;
+  protected com.fluendo.jst.Object parent;
+  protected int flags;
+
+  public static final int OBJECT_FLAG_LAST = (1 << 4);
+
+  public synchronized String getName () {
+    return name;
+  }
+  public synchronized void setName (String newName) {
+    name = newName;
+  }
+  public Object() {
+    this("unnamed");
+  }
+  public Object(String name) {
+    super();
+    this.name = name;
+    parent = null;
+  }
+  public synchronized boolean setParent (com.fluendo.jst.Object newParent) {
+    if (parent != null)
+      return false;
+
+    parent = newParent;
+
+    return true;
+  }
+  public synchronized com.fluendo.jst.Object getParent () {
+    return parent;
+  }
+  public synchronized void unParent () {
+    parent = null;
+  }
+
+  public synchronized void setFlag(int flag)
+  {
+    flags |= flag;
+  }
+  public synchronized void unsetFlag(int flag)
+  {
+    flags &= ~flag;
+  }
+  public synchronized boolean isFlagSet(int flag)
+  {
+    return (flags & flag) == flag;
+  }
+
+  public synchronized boolean setProperty(String name, java.lang.Object value)
+  {
+    return false;
+  }
+  public synchronized java.lang.Object getProperty(String name)
+  {
+    return null;
+  }
+}

Added: trunk/cronus/com/fluendo/jst/Pad.java
===================================================================
--- trunk/cronus/com/fluendo/jst/Pad.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jst/Pad.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,388 @@
+/* Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.jst;
+
+import java.util.*;
+import com.fluendo.utils.*;
+
+public class Pad extends com.fluendo.jst.Object implements Runnable
+{
+  /* pad directions */
+  public static final int UNKNOWN = 0;
+  public static final int SRC = 1;
+  public static final int SINK = 2;
+
+  /* flow return values */
+  public static final int OK = 0;
+  public static final int NOT_LINKED = -1;
+  public static final int WRONG_STATE = -2;
+  public static final int UNEXPECTED = -3;
+  public static final int NOT_NEGOTIATED = -4;
+  public static final int ERROR = -5;
+  public static final int NOT_SUPPORTED = -6;
+
+  /* modes */
+  public static final int MODE_NONE = 0;
+  public static final int MODE_PUSH = 1;
+  public static final int MODE_PULL = 2;
+
+  protected Pad peer;
+  protected int direction = UNKNOWN;
+  protected boolean flushing;
+  protected java.lang.Object streamLock =  new java.lang.Object();
+  int mode;
+  private Vector capsListeners = new Vector();
+
+  protected Caps caps;
+  
+  /* task stuff */
+  private static final int T_STOP = 0;
+  private static final int T_PAUSE = 1;
+  private static final int T_START = 2;
+  private Thread thread;
+  private int taskState;
+
+  public static final boolean isFlowFatal (int ret)
+  {
+    return ret <= UNEXPECTED;
+  }
+  public static final boolean isFlowSuccess (int ret)
+  {
+    return ret >= OK;
+  }
+  public static final String getFlowName (int ret) {
+    switch (ret) {
+      case OK:
+        return "ok";
+      case NOT_LINKED:
+        return "not-linked";
+      case WRONG_STATE:
+        return "wrong-state";
+      case UNEXPECTED:
+        return "unexpected";
+      case NOT_NEGOTIATED:
+        return "not-negotiated";
+      case ERROR:
+        return "error";
+      case NOT_SUPPORTED:
+        return "not-supported";
+      default:
+        return "unknown";
+    }
+  }
+
+  public Pad(int direction) {
+    this (direction, null);
+  }
+  public Pad(int direction, String name) {
+    super(name);
+    this.direction = direction;
+  }
+
+  public String toString () {
+    String parentName;
+    String thisName;
+
+    if (parent != null)
+      parentName = parent.getName();
+    else
+      parentName = "";
+
+    thisName = getName();
+    if (thisName == null)
+      thisName="";
+
+    //return "Pad: "+parentName+":"+thisName+" ["+super.toString()+"]";
+    return "Pad: "+parentName+":"+thisName;
+  }
+
+  public synchronized void addCapsListener(CapsListener listener)
+  {
+    capsListeners.addElement (listener);
+  }
+  public synchronized void removeCapsListener(CapsListener listener)
+  {
+    capsListeners.removeElement (listener);
+  }
+  private synchronized void doCapsListeners(Caps caps)
+  {
+    for (Enumeration e = capsListeners.elements(); e.hasMoreElements();) {
+      CapsListener listener = (CapsListener) e.nextElement();
+      listener.capsChanged (caps);
+    }
+  }
+
+  public synchronized boolean link (Pad newPeer) {
+
+    /* already was connected */
+    if (peer != null)
+      return false;
+
+    /* wrong direction */
+    if (direction != SRC)
+      return false;
+
+    synchronized (newPeer) {
+      if (newPeer.direction != SINK)
+	return false;
+
+      /* peer was connected */
+      if (newPeer.peer != null)
+	return false;
+
+      peer = newPeer;
+      peer.peer = this;
+    }
+    return true;
+  }
+
+  public synchronized void unlink () {
+    if (peer == null)
+      return;
+
+    if (direction == SRC) {
+      peer.unlink ();
+    }
+    peer = null;
+  }
+
+  public synchronized Pad getPeer () {
+    return peer;
+  }
+
+  protected boolean eventFunc (Event event)
+  {
+    boolean result;
+
+    switch (event.getType()) {
+      case Event.FLUSH_START:
+      case Event.FLUSH_STOP:
+      case Event.EOS:
+      case Event.SEEK:
+      case Event.NEWSEGMENT:
+      default:
+        result = false;
+        break;
+    }
+    return result;
+  }
+
+  public final boolean sendEvent (Event event) {
+    boolean result;
+
+    Debug.debug(this + " got event " + event);
+
+    switch (event.getType()) {
+      case Event.FLUSH_START:
+        setFlushing (true);
+        result = eventFunc (event);
+        break;
+      case Event.FLUSH_STOP:
+        synchronized (streamLock) {
+          setFlushing (false);
+          result = eventFunc (event);
+	}
+        break;
+      case Event.NEWSEGMENT:
+      case Event.EOS:
+        synchronized (streamLock) {
+	  result = eventFunc (event);
+	}
+        break;
+      case Event.SEEK:
+		result = eventFunc (event);
+        break;
+      default:
+        result = false;
+        break;
+    }
+    return result;
+  }
+
+  public boolean query (Query query) {
+    return false;
+  }
+
+  public synchronized Caps getCaps () {
+    return this.caps;
+  }
+
+  protected boolean setCapsFunc (Caps caps) {
+    return true;
+  }
+
+  public boolean setCaps (Caps caps) {
+    boolean res = true;
+
+    if (caps != null)
+      res = setCapsFunc (caps);
+
+    if (res) {
+      this.caps = caps;
+      if (caps != null)
+        doCapsListeners (caps);
+    }
+    return res;
+  }
+
+  private final int chain (Buffer buffer) {
+    synchronized (streamLock) {
+      synchronized (this) {
+        if (flushing)
+	  return WRONG_STATE;
+
+	if (buffer.caps != null && buffer.caps != caps) {
+	  if (!setCaps(buffer.caps)) {
+		  buffer.close();
+		  buffer = null;
+	    return NOT_NEGOTIATED;
+	  }
+	}
+	  }
+		int res = chainFunc(buffer);
+      return res;
+    }
+  }
+
+  protected int chainFunc (Buffer buffer)
+  {
+    return ERROR;
+  }
+
+public final int push (Buffer buffer) {
+    if (peer == null) {
+      return NOT_LINKED;
+    }
+    return peer.chain (buffer);
+}
+
+  public final boolean pushEvent (Event event) {
+    if (peer == null)
+      return false;
+
+    return peer.sendEvent (event);
+  }
+
+  public synchronized void setFlushing (boolean flush) {
+    flushing = flush;
+  }
+  public synchronized boolean isFlushing () {
+    return flushing;
+  }
+
+  protected boolean activateFunc (int mode)
+	{
+    return true;
+  }
+
+  public final boolean activate (int newMode)
+  {
+    boolean res;
+
+    Debug.debug(this + " activate mode = " + ( newMode == MODE_NONE ? "none" : "push" ));
+
+    if (mode == newMode)
+      return true;
+
+    if (newMode == MODE_NONE) {
+      setFlushing (true);
+    }
+    if ((res = activateFunc (newMode)) == false)
+      return false;
+
+    if (newMode != MODE_NONE) {
+      setFlushing (false);
+    }
+    else {
+      synchronized (streamLock) {
+        setCaps (null);
+      }
+    }
+    mode = newMode;
+
+    return res;
+  }
+
+  protected void taskFunc()
+  {
+  }
+
+  public void run() {
+    synchronized (streamLock) {
+      while (taskState != T_STOP) {
+        while (taskState == T_PAUSE) {
+	  Debug.debug(parent.getName() + ":" + this.getName() + " paused, waiting...");
+	  try {
+	    streamLock.wait();
+	  }
+	  catch (InterruptedException ie) {}
+	}
+        if (taskState == T_STOP)
+	  break;
+
+	try {
+          taskFunc();
+	}
+	catch (Throwable t) {
+          t.printStackTrace();
+	}
+      }
+    }
+  }
+
+  public boolean startTask(String name)
+  {
+    synchronized (streamLock) {
+      taskState = T_START;
+      if (thread == null) {
+        thread = new Thread(this, name);
+        thread.start();
+      }
+      streamLock.notifyAll();
+    }
+    return true;
+  }
+  public boolean pauseTask()
+  {
+    taskState = T_PAUSE;
+    synchronized (streamLock) {
+      taskState = T_PAUSE;
+    }
+    return true;
+  }
+
+  public boolean stopTask()
+  {
+    Thread t;
+	  
+    taskState = T_STOP;
+    synchronized (streamLock) {
+      taskState = T_STOP;
+      streamLock.notifyAll();
+      t = thread;
+		thread = null;
+    }
+	  try {
+      t.join();
+    }
+    catch (InterruptedException ie) {}
+
+    return true;
+  }
+}

Added: trunk/cronus/com/fluendo/jst/PadListener.java
===================================================================
--- trunk/cronus/com/fluendo/jst/PadListener.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jst/PadListener.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,26 @@
+/* Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.jst;
+
+public interface PadListener
+{
+  public void padAdded (Pad pad);
+  public void padRemoved (Pad pad);
+  public void noMorePads ();
+}

Added: trunk/cronus/com/fluendo/jst/Pipeline.java
===================================================================
--- trunk/cronus/com/fluendo/jst/Pipeline.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jst/Pipeline.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,640 @@
+/* Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.jst;
+
+import java.util.*;
+import com.fluendo.utils.*;
+
+public class Pipeline extends com.fluendo.jst.Element implements BusSyncHandler
+{
+  protected Vector elements = new Vector();
+
+  protected Clock defClock;
+  protected Clock fixedClock = null;
+  protected Element clockProvider;
+  protected Vector messages = new Vector();
+
+  protected Bus internalBus;
+  private BusThread busThread;
+
+  private StateThread stateThread;
+  private boolean stateDirty = false;
+  private boolean polling = false;
+
+  protected long streamTime;
+
+  private class BusThread extends Thread
+  {
+    private Bus bus;
+    private boolean stopping;
+
+    public BusThread (Bus bus)
+    {
+      super("cortado-BusThread-"+Debug.genId());
+      this.bus = bus;
+      stopping = false;
+    }
+    public void run() {
+      while (!stopping) {
+        bus.waitAndDispatch ();
+      }
+    }
+    public void shutDown() {
+      stopping = true;
+      bus.setFlushing(true);
+    }
+  }
+
+  private class StateThread extends Thread
+  {
+    private boolean stopping;
+    private boolean stateDirty;
+
+    public StateThread ()
+    {
+      super("cortado-StateThread-"+Debug.genId());
+      stopping = false;
+      stateDirty = false;
+    }
+    public void run() {
+      while (!stopping) {
+        synchronized (this) {
+          while (!stateDirty && !stopping) {
+	    try {
+              wait();
+	    } catch (InterruptedException e) {}
+	  }
+          stateDirty = false;
+        }
+	if (!stopping) {
+          synchronized (stateLock) {
+            reCalcState(false);
+          }
+	}
+      }
+    }
+    public synchronized void stateDirty() {
+      stateDirty = true;
+      notifyAll ();
+    }
+    public synchronized void shutDown() {
+      stopping = true;
+      notifyAll ();
+    }
+  }
+
+  public Pipeline() {
+    this (null);
+  }
+
+  public String getFactoryName () {
+    return "pipeline";
+  }
+
+  public Pipeline(String name) {
+    super (name);
+
+    defClock = new SystemClock();
+    clockProvider = null;
+
+    internalBus = new Bus();
+    internalBus.setSyncHandler (this);
+    bus = new Bus();
+    busThread = new BusThread(bus);
+    busThread.start();
+    stateThread = new StateThread();
+    stateThread.start();
+  }
+
+  public synchronized void shutDown() {
+    if (stateThread != null) {
+      stateThread.shutDown();
+      stateThread = null;
+    }
+    if (busThread != null) {
+      busThread.shutDown();
+      busThread = null;
+    }
+  }
+
+  public void useClock(Clock clock) {
+    fixedClock = clock;
+  }
+
+  public boolean add(Element elem) {
+    if (elem == null)
+      return false;
+
+    if (elem instanceof ClockProvider) {
+      defClock = ((ClockProvider)elem).provideClock();
+      clockProvider = elem;
+    }
+
+    elements.addElement (elem);
+    elem.baseTime = baseTime;
+    elem.setBus (internalBus);
+    return true;
+  }
+  public boolean remove(Element elem) {
+    boolean res;
+
+    if (elem == null)
+      return false;
+
+    if ((res = elements.removeElement (elem))) {
+      if (elem == clockProvider) {
+        defClock = new SystemClock();
+        clockProvider = null;
+      }
+      elem.setBus (null);
+      elem.setClock (null);
+      synchronized (this) {
+        stateDirty = true;
+      }
+    }
+    return res;
+  }
+
+  public Enumeration enumElements()
+  {
+    return elements.elements();
+  }
+
+  private class SortedEnumerator implements Enumeration
+  {
+    private Vector queue;
+    private Hashtable hash;
+    private java.lang.Object next;
+    private int mode;
+
+    private void addToQueue (Element elem)
+    {
+      queue.addElement (elem);
+      hash.put (elem, new Integer(-1));
+    }
+
+    private void updateDegree (Element elem)
+    {
+      for (Enumeration p = elem.enumPads(); p.hasMoreElements();) {
+        Pad pad = (Pad) p.nextElement();
+
+	if (pad.direction == Pad.SINK) {
+	  Pad peer;
+	  Element peerParent;
+	  int oldDeg, newDeg;
+
+	  peer = pad.peer;
+	  if (peer == null)
+	    continue;
+
+	  peerParent = (Element) peer.parent;
+	  if (peerParent == null)
+	    continue;
+
+	  oldDeg = ((Integer)hash.get (peerParent)).intValue();
+	  newDeg = oldDeg + mode;
+
+	  if (newDeg == 0) {
+	    addToQueue (peerParent);
+	  }
+	  else {
+	    hash.put (peerParent, new Integer (newDeg));
+	  }
+	}
+      }
+    }
+
+    public SortedEnumerator() {
+      queue = new Vector();
+      hash = new Hashtable();
+
+      /* reset all degrees, add sinks to queue */
+      for (Enumeration e = enumElements(); e.hasMoreElements();) {
+        Element elem = (Element) e.nextElement();
+
+        if (elem.isFlagSet (Element.FLAG_IS_SINK)) {
+	  addToQueue (elem);
+	}
+	else {
+	  hash.put (elem, new Integer(0));
+	}
+      }
+      mode = 1;
+      /* update all degrees */
+      for (Enumeration e = enumElements(); e.hasMoreElements();) {
+        updateDegree ((Element) e.nextElement());
+      }
+      mode = -1;
+      queueNextElement();
+    }
+    private void queueNextElement ()
+    {
+      
+      if (queue.isEmpty()) {
+        int bestDeg = Integer.MAX_VALUE;
+	Element bestElem = null;
+
+        for (Enumeration e = enumElements(); e.hasMoreElements();) {
+          Element elem = (Element) e.nextElement();
+	  int deg;
+
+	  deg = ((Integer)hash.get (elem)).intValue();
+	  if (deg < 0)
+	    continue;
+
+	  if (bestElem == null || bestDeg > deg) {
+	    bestElem = elem;
+	    bestDeg = deg;
+	  }
+	}
+	if (bestElem != null) {
+	  if (bestDeg != 0) {
+	    System.out.println (this+" loop detected in pipeline!!");
+	  }
+	  next = bestElem;
+	  hash.put (next, new Integer(-1));
+	}
+	else {
+	  next = null;
+	}
+      }
+      else {
+        next = queue.elementAt (0);
+        queue.removeElementAt (0);
+      }
+      if (next != null)
+        updateDegree ((Element) next);
+    }
+
+    public boolean hasMoreElements()
+    {
+      return next != null;
+    }
+    public java.lang.Object nextElement() throws NoSuchElementException
+    {
+      java.lang.Object result = next;
+
+      if (result == null)
+        throw new NoSuchElementException();
+      
+      queueNextElement ();
+
+      return result;
+    }
+  }
+
+  public Enumeration enumSorted()
+  {
+    return new SortedEnumerator();
+  }
+  
+  private class SinkEnumerator implements Enumeration
+  {
+    private Enumeration e;
+    private java.lang.Object next;
+
+    public SinkEnumerator() {
+      e = enumElements();
+      queueNextElement();
+    }
+    private void queueNextElement()
+    {
+      next = null;
+      while (e.hasMoreElements()) {
+        Element elem = (Element) e.nextElement();
+
+        if (elem.isFlagSet (Element.FLAG_IS_SINK)) {
+          next = elem;
+	  break;
+	}
+      }
+    }
+
+    public boolean hasMoreElements()
+    {
+      return next != null;
+    }
+    public java.lang.Object nextElement() throws NoSuchElementException
+    {
+      java.lang.Object result = next;
+
+      if (result == null)
+        throw new NoSuchElementException();
+      
+      queueNextElement ();
+
+      return result;
+    }
+  }
+
+  public Enumeration enumSinks()
+  {
+    return new SinkEnumerator();
+  }
+
+  private void replaceMessage (Message message, int type)
+  {
+    int len = messages.size();
+    Message msg;
+    com.fluendo.jst.Object src = message.getSrc();
+
+    for (int i=0; i<len; i++) {
+      msg = (Message) messages.elementAt(i);
+
+      if (msg.getType() == type && msg.getSrc() == src) {
+	messages.setElementAt(message, i);
+	return;
+      }
+    }
+    messages.addElement(message);
+  }
+
+  private boolean findMessage (com.fluendo.jst.Object obj, int type)
+  {
+    int len = messages.size();
+    Message msg;
+
+    for (int i=0; i<len; i++) {
+      msg = (Message) messages.elementAt(i);
+
+      if (msg.getType() == type && msg.getSrc() == obj)
+	return true;
+    }
+    return false;
+  }
+
+  protected boolean isEOS ()
+  {
+    com.fluendo.jst.Object obj;
+
+    for (Enumeration e = enumSinks(); e.hasMoreElements();) {
+      obj = (com.fluendo.jst.Object) e.nextElement();
+
+      if (!findMessage (obj, Message.EOS))
+	return false;
+    }
+    return true;
+  }
+
+  public int handleSyncMessage (Message message)
+  {
+    switch (message.getType()) {
+      case Message.EOS:
+      {
+	boolean isEOS;
+
+	synchronized (this) {
+          Debug.log(Debug.INFO, this+" got EOS from sink: "+message.getSrc());
+          replaceMessage (message, Message.EOS);
+	  isEOS = isEOS();
+	}
+	if (isEOS) {
+          Debug.log(Debug.INFO, "all sinks posted EOS "+this);
+          postMessage (Message.newEOS (this));
+	}
+        break;
+      }
+      case Message.STATE_DIRTY:
+	scheduleReCalcState ();
+        break;
+      default:
+        /* post to app */
+        postMessage (message);
+        break;
+    }
+    return BusSyncHandler.DROP;
+  }
+
+  public int getState(int[] resState, int[] resPending, long timeout) {
+    reCalcState (false);
+    return super.getState (resState, resPending, timeout);
+  }
+
+  protected void scheduleReCalcState() {
+    synchronized (this) {
+      stateDirty = true;
+      stateThread.stateDirty();
+    }
+  }
+
+  private void reCalcState(boolean force)
+  {
+    boolean haveAsync, haveNoPreroll;
+    int res = SUCCESS;
+
+    Debug.debug("Pipeline.reCalcState");
+
+    synchronized (this) {
+      if (force)
+        stateDirty = true;
+      
+      if (!stateDirty)
+        return;
+
+      if (polling)
+        return;
+ 
+      polling = true;
+      stateDirty = false;
+      haveAsync = false;
+      haveNoPreroll = false;
+    }
+    for (Enumeration e = elements.elements(); e.hasMoreElements();) {
+      Element elem = (Element) e.nextElement();
+
+      res = elem.getState(null, null, 1);
+      switch (res) {
+        case ASYNC:
+	  haveAsync = true;
+          break;
+        case NO_PREROLL:
+	  haveNoPreroll = true;
+          break;
+      }
+      if (res == FAILURE)
+        break;
+    }
+
+    if (res != FAILURE) {
+      if (haveNoPreroll)
+        res = NO_PREROLL;
+      if (haveAsync)
+        res = ASYNC;
+    }
+
+    synchronized (this) {
+      polling = false;
+    }
+
+    switch (res) {
+      case SUCCESS:
+      case NO_PREROLL:
+        res = continueState(res);
+        break;
+      case ASYNC:
+	lostState();
+        break;
+      case FAILURE:
+        abortState();
+        break;
+      default:
+        break;
+    }
+    return;
+  }
+
+  protected int doChildStateChange(int transition)
+  {
+    int next;
+    int result;
+    boolean haveAsync, haveNoPreroll;
+
+    next = getTransitionNext (transition);
+
+    haveAsync = false;
+    haveNoPreroll = false;
+
+    for (Enumeration e = enumSorted(); e.hasMoreElements();) {
+      Element elem = (Element) e.nextElement();
+
+      elem.setBus (internalBus);
+      elem.setClock (defClock);
+      elem.baseTime = baseTime;
+
+      Debug.log(Debug.DEBUG, this+" setting state " + getStateName(next) + " on "+elem);
+      result = elem.setState (next);
+      Debug.log(Debug.DEBUG, this+" "+elem+" changed state, result = " + getStateReturnName(result));
+
+      switch (result) {
+        case ASYNC:
+          haveAsync = true;
+	  break;
+	case NO_PREROLL:
+          haveNoPreroll = true;
+	  break;
+	case FAILURE:
+          return result;
+      }
+    }
+
+    result = super.changeState(transition);
+    if (result == FAILURE)
+      return result;
+
+    if (haveNoPreroll)
+      result = NO_PREROLL;
+    else if (haveAsync)
+      result = ASYNC;
+
+    return result;
+  }
+
+  protected int changeState(int transition)
+  {
+    int result;
+
+    switch (transition) {
+      case STOP_PAUSE:
+	messages.setSize(0);
+        break;
+      case PAUSE_PLAY:
+        long now = defClock.getTime();
+        baseTime = now - streamTime;
+        break;
+      default:
+        break;
+    }
+
+    result = doChildStateChange(transition);
+
+    switch (transition) {
+      case STOP_PAUSE:
+        streamTime = 0;
+	break;
+      case PLAY_PAUSE:
+        long now = defClock.getTime();
+        streamTime = now - baseTime;
+	messages.setSize(0);
+        break;
+      case PAUSE_STOP:
+	messages.setSize(0);
+        break;
+      default:
+        break;
+    }
+
+    return result;
+  }
+
+  protected boolean doSendEvent(Event event)
+  {
+    boolean res = true;
+
+    for (Enumeration e = enumSinks(); e.hasMoreElements();) {
+      Element elem = (Element) e.nextElement();
+
+      res &= elem.sendEvent (event);
+    }
+    return res;
+  }
+
+  private boolean doSeek(Event event)
+  {
+    boolean ret;
+    int[] state = new int[1];
+    boolean wasPlaying;
+
+    getState(state, null, 0);
+    wasPlaying = (state[0] == Element.PLAY);
+
+    if (wasPlaying)
+      setState (Element.PAUSE);
+
+    ret = doSendEvent (event);
+    if (ret)
+      streamTime = 0;
+    if (wasPlaying)
+      setState (Element.PLAY);
+
+    return ret;
+  }
+
+  public boolean sendEvent(Event event)
+  {
+    switch (event.getType()) {
+      case Event.SEEK:
+        return doSeek (event);
+      default:
+        return doSendEvent (event);
+    }
+  }
+
+  public boolean query(Query query)
+  {
+    boolean res = true;
+
+    for (Enumeration e = enumSinks(); e.hasMoreElements();) {
+      Element elem = (Element) e.nextElement();
+
+      if ((res = elem.query (query)))
+        break;
+    }
+    return res;
+  }
+}

Added: trunk/cronus/com/fluendo/jst/Query.java
===================================================================
--- trunk/cronus/com/fluendo/jst/Query.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jst/Query.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,78 @@
+/* Cortado - a video player java applet
+ * Copyright (C) 2004 Fluendo S.L.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.jst;
+
+public class Query {
+
+  /* types */
+  public static final int POSITION = 1;
+  public static final int DURATION = 2;
+  public static final int LATENCY = 3;
+  public static final int JITTER = 4;
+  public static final int RATE = 5;
+  public static final int SEEKING = 6;
+  public static final int SEGMENT = 7;
+  public static final int CONVERT = 8;
+  public static final int FORMATS = 9;
+
+  private int type;
+  private int format;
+  private long value;
+
+  private Query(int type) {
+    value = -1;
+    this.type = type;
+  }
+
+  public int getType () {
+    return type;
+  }
+
+  public static Query newPosition(int format) {
+    Query q = new Query(POSITION);
+    q.format = format;
+    return q;
+  }
+  public void setPosition(int format, long position) {
+    this.format = format;
+    this.value = position;
+  }
+  public int parsePositionFormat() {
+    return format;
+  }
+  public long parsePositionValue() {
+    return value;
+  }
+
+  public static Query newDuration(int format) {
+    Query q = new Query(DURATION);
+    q.format = format;
+    return q;
+  }
+  public void setDuration(int format, long position) {
+    this.format = format;
+    this.value = position;
+  }
+  public int parseDurationFormat() {
+    return format;
+  }
+  public long parseDurationValue() {
+    return value;
+  }
+}

Added: trunk/cronus/com/fluendo/jst/Sink.java
===================================================================
--- trunk/cronus/com/fluendo/jst/Sink.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jst/Sink.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,456 @@
+/* Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.jst;
+
+import com.fluendo.utils.*;
+
+public abstract class Sink extends Element
+{
+  private java.lang.Object prerollLock = new java.lang.Object();
+  private boolean isEOS;
+  private boolean flushing;
+  private boolean havePreroll;
+  private boolean needPreroll;
+  private Clock.ClockID clockID;
+  protected boolean discont;
+  protected long segStart = 0;
+  protected long segStop;
+  protected long segPosition;
+  protected long pauseTime;
+  protected long lastTime;
+
+  // Maximum lateness before the buffer is dropped, or -1 for no limit
+  // property max-lateness
+  protected long maxLateness = -1;
+
+  protected Pad sinkpad = new Pad(Pad.SINK, "sink") {
+    private int finishPreroll(Buffer buf)
+    {
+      synchronized (prerollLock) {
+        int res = OK;
+	Sink sink = (Sink) parent;
+
+	if (isFlushing())
+	  return WRONG_STATE;
+
+        if (needPreroll) {
+
+	  havePreroll = true;
+          try {
+            res = preroll (buf);
+	  }
+	  catch (Throwable t) {
+	    postMessage (Message.newError (this, "preroll exception: "+t.getMessage()));
+	    return Pad.ERROR;
+	  }
+
+	  boolean postPause = false;
+	  boolean postPlaying = false;
+	  int current, next, pending, postPending;
+
+	  synchronized (sink) {
+	    current = currentState;
+	    next = nextState;
+	    pending = pendingState;
+	    postPending = pending;
+
+	    switch (pending) {
+	      case PLAY:
+	        needPreroll = false;
+		postPlaying = true;
+		if (current == STOP)
+		  postPause = true;
+		break;
+	      case PAUSE:
+	        needPreroll = true;
+		postPause = true;
+		postPending = NONE;
+		break;
+	      case STOP:
+	        havePreroll = false;
+	        needPreroll = false;
+	        return WRONG_STATE;
+	      case NONE:
+                switch (current) {
+                  case PLAY:
+                    needPreroll = false;
+                    break;
+                  case PAUSE:
+                    needPreroll = true;
+                    break;
+                  default:
+	            havePreroll = false;
+	            needPreroll = false;
+                    return WRONG_STATE;
+                }
+		break;
+	    }
+	    if (pending != NONE) {
+	      currentState = pending;
+	      nextState = NONE;
+	      pendingState = NONE;
+	      lastReturn = SUCCESS;
+	    }
+	  }
+
+	  if (postPause)
+	    postMessage (Message.newStateChanged (this, current, next, postPending));
+	  if (postPlaying)
+	    postMessage (Message.newStateChanged (this, next, pending, NONE));
+
+	  if (postPause || postPlaying)
+	    postMessage (Message.newStateDirty (this));
+
+	  synchronized (sink) {
+	    sink.notifyAll();
+	  }
+
+	  if (needPreroll) {
+	    needPreroll = false;
+	    try {
+	      prerollLock.wait();
+	    }
+	    catch (InterruptedException ie) {}
+
+	    havePreroll = false;
+	  }
+	}
+	if (isFlushing())
+	  return WRONG_STATE;
+
+	return res;
+      }
+    }
+
+    protected boolean eventFunc (Event event)
+    {
+      Sink sink = (Sink) parent;
+      doEvent(event);
+
+      switch (event.getType()) {
+        case Event.FLUSH_START:
+	  synchronized (sink) {
+	    sink.flushing = true;
+	    if (clockID != null) {
+	      clockID.unschedule();
+	    }
+	  }
+	  synchronized (prerollLock) {
+	    sink.isEOS = false;
+	    needPreroll = true;
+	    prerollLock.notify();
+	    havePreroll = false;
+	  }
+	  synchronized (streamLock) {
+	    Debug.debug(this+" synced "+havePreroll+" "+needPreroll);
+	    lostState();
+	  }
+	  break;
+        case Event.FLUSH_STOP:
+	  synchronized (sink) {
+	    sink.flushing = false;
+	    pauseTime = 0;
+	  }
+	  break;
+        case Event.NEWSEGMENT:
+	  int segFmt = event.parseNewsegmentFormat();
+	  if (segFmt == Format.TIME) {
+	    segStart = event.parseNewsegmentStart();
+	    segStop = event.parseNewsegmentStop();
+	    segPosition = event.parseNewsegmentPosition();
+	    lastTime = segPosition;
+	  }
+	  break;
+        case Event.EOS:
+          synchronized (prerollLock) {
+	    isEOS = true;
+	    Debug.log(Debug.INFO, this+" got EOS");
+	    postMessage (Message.newEOS (parent));
+	  }
+	  break;
+	default:
+	  break;
+      }
+
+      return true;
+    }
+  
+    protected int chainFunc (Buffer buf)
+    {
+      int res;
+      WaitStatus status;
+      long time;
+
+      if (buf.isFlagSet (com.fluendo.jst.Buffer.FLAG_DISCONT))
+        discont = true;
+
+      time = buf.timestamp;
+
+      Debug.debug(parent.getName() + " <<< " + time);
+
+      /* clip to segment */
+      if (time != -1) {
+	if (time < segStart) {
+	  Debug.debug(parent.getName() + " " + time + " >>> PRE-SEGMENT DROP" );
+	  buf.close();
+          return OK;
+	}
+	else {
+          lastTime = time - segStart + segPosition;
+	}
+      }
+
+      buf.setFlag (com.fluendo.jst.Buffer.FLAG_DISCONT, discont);
+      discont = false;
+
+      if ((res = finishPreroll(buf)) != Pad.OK) {
+	Debug.debug(parent.getName() + " " + time + " >>> PREROLL DROP" );
+        return res;
+      }
+
+      Debug.debug(parent.getName() + " sync " + time );
+      status = doSync(time);
+      switch (status.status) {
+        case WaitStatus.LATE:
+	  if (maxLateness != -1 && status.jitter > maxLateness) {
+	    Debug.debug(parent.getName() + " " + time + " >>> LATE, DROPPED" );
+	    break;
+	  }
+	  // Not too late, fall through...
+        case WaitStatus.OK:
+          try {
+	    Debug.debug(parent.getName() + " >>> " + time);
+            res = render (buf);
+	  }
+	  catch (Throwable t) {
+	    postMessage (Message.newError (this, "render exception: "+t.getMessage()));
+	    res = Pad.ERROR;
+	  }
+	  break;
+	default:
+	  Debug.debug(parent.getName() + " " + time + " >>> SYNC DROP" );
+	  res = Pad.OK;
+	  break;
+      }
+      buf.close();
+
+      return res;
+    }
+
+    protected boolean setCapsFunc (Caps caps)
+    {
+      boolean res;
+      Sink sink = (Sink) parent;
+	
+      res = sink.setCapsFunc (caps);
+
+      return res;
+    }
+    protected boolean activateFunc (int mode)
+    {
+      if (mode == MODE_NONE) {
+        synchronized (prerollLock) {
+	  if (havePreroll) {
+	    prerollLock.notify();
+	  }
+	  needPreroll = false;
+	  havePreroll = false;
+	  this.flushing = true;
+	}
+	isEOS = false;
+      }
+      else {
+	this.flushing = false;
+      }
+      return true;
+    }
+  };
+
+  protected int preroll (Buffer buf) {
+    return Pad.OK;
+  }
+
+  protected boolean doEvent(Event event)
+  {
+    return true;
+  }
+
+  protected WaitStatus doSync(long time) {
+    WaitStatus ret = new WaitStatus();
+    Clock.ClockID id = null;
+
+    synchronized (this) {
+      if (flushing) {
+	ret.status = WaitStatus.UNSCHEDULED;
+	return ret;
+      }
+
+      if (time == -1) {
+	ret.status = WaitStatus.OK;
+	return ret;
+      }
+
+      time = time - segStart + baseTime;
+
+      if (clock != null)
+        id = clockID = clock.newSingleShotID (time);
+    }
+    
+    if (id != null) {
+      ret = id.waitID();
+    }
+    else
+      ret.status = WaitStatus.OK;
+
+    synchronized (this) {
+      clockID = null;
+    }
+    return ret;
+  }
+  protected boolean setCapsFunc (Caps caps) {
+    return true;
+  }
+
+  protected int render (Buffer buf) {
+    return Pad.OK;
+  }
+
+  public Sink () {
+    super ();
+    addPad (sinkpad);
+    setFlag (Element.FLAG_IS_SINK);
+  }
+
+  public boolean sendEvent (Event event) {
+    return sinkpad.pushEvent (event);
+  }
+
+  public boolean query (Query query) {
+    switch (query.getType()) {
+      case Query.DURATION:
+        return sinkpad.getPeer().query (query);
+      case Query.POSITION:
+      {
+	long position = -1;
+        if (query.parsePositionFormat() == Format.TIME) {
+          synchronized (this) {
+	    if (currentState == PLAY) {
+	      if (clock != null) {
+	        position = clock.getTime() - baseTime + segPosition + segStart;
+	      }
+	    }
+	    else {
+	      position = pauseTime + segPosition + segStart;
+	    }
+	  }
+	  query.setPosition(Format.TIME, position);
+	}
+	else {
+          return sinkpad.getPeer().query (query);
+	}
+        break;
+      }
+      default:
+        return sinkpad.getPeer().query (query);
+    }
+    return true;
+  }
+
+  protected int changeState (int transition) {
+    int result = SUCCESS;
+    int presult;
+
+    switch (transition) {
+      case STOP_PAUSE:
+	this.isEOS = false;
+        synchronized (prerollLock) {
+          needPreroll = true;
+          havePreroll = false;
+        }
+        result = ASYNC;
+        break;
+      case PAUSE_PLAY:
+        synchronized (prerollLock) {
+          if (havePreroll) {
+            needPreroll = false;
+	    prerollLock.notify();
+	  }
+	  else {
+            needPreroll = false;
+	  }
+	}
+        break;
+      case PLAY_PAUSE:
+        synchronized (this) {
+	  pauseTime = clock.getTime() - baseTime;
+	}
+        break;
+      default:
+        break;
+    }
+
+    presult = super.changeState(transition);
+    if (presult == FAILURE) {
+      Debug.debug(this+" super state change failed");
+      return presult;
+    }
+
+    switch (transition) {
+      case PLAY_PAUSE:
+      {
+        boolean checkEOS;
+        Debug.debug(this+" play->paused");
+
+        /* unlock clock */
+        synchronized (this) {
+	  if (clockID != null) {
+            Debug.debug(this+" unschedule clockID: "+ clockID);
+	    clockID.unschedule();
+	  }
+	  checkEOS = this.isEOS;
+          Debug.debug(this+" checkEOS: "+ checkEOS);
+	}
+        synchronized (prerollLock) {
+          Debug.debug(this+" havePreroll: "+ havePreroll);
+	  if (!havePreroll && !checkEOS && pendingState == PAUSE) {
+	    needPreroll = true;
+	    result = ASYNC;
+	  }
+	}
+        break;
+      }
+      case PAUSE_STOP:
+        break;
+      default:
+        break;
+    }
+
+    return result;
+  }
+
+  public synchronized boolean setProperty(String name, java.lang.Object value) {
+    boolean res = true;
+    if (name.equals("max-lateness")) {
+      maxLateness = Long.parseLong((String)value);
+    } else {
+      res = false;
+    }
+    return res;
+  }
+}

Added: trunk/cronus/com/fluendo/jst/SourceInfo.java
===================================================================
--- trunk/cronus/com/fluendo/jst/SourceInfo.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jst/SourceInfo.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,10 @@
+package com.fluendo.jst;
+
+public class SourceInfo
+{
+  public String revision = "0.5.0-5-gcc869bc";
+  public String branch = "Xiph";
+
+  public SourceInfo() {
+  }
+}

Added: trunk/cronus/com/fluendo/jst/SystemClock.java
===================================================================
--- trunk/cronus/com/fluendo/jst/SystemClock.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jst/SystemClock.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,83 @@
+/* Cortado - a video player java applet
+ * Copyright (C) 2004 Fluendo S.L.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.jst;
+
+import com.fluendo.utils.*;
+
+public class SystemClock extends Clock {
+  protected long getInternalTime()
+  {
+    return System.currentTimeMillis() * Clock.MSECOND;	  
+  }
+  protected WaitStatus waitFunc(ClockID id)
+  {
+    WaitStatus res = new WaitStatus();
+
+    long real = getInternalTime();
+    long entryt = id.time;
+    long now = adjust (real);
+    long systemTime = System.currentTimeMillis();
+
+    res.jitter = now - entryt;
+
+    if (res.jitter < 0) {
+      Debug.log(Debug.DEBUG, "Waiting from "+now+" until "+entryt+" ("+(-res.jitter)+"us)");
+      long millis;
+      int nanos;
+
+      millis = -res.jitter / Clock.MSECOND;
+      nanos = (int) ((-res.jitter % Clock.MSECOND) * Clock.MSECOND);
+
+      synchronized (this) {
+        if (id.status == WaitStatus.UNSCHEDULED) {
+	  res.status = WaitStatus.UNSCHEDULED;
+	  return res;
+	}
+
+        id.status = WaitStatus.OK;
+        try {
+          wait (millis, nanos);
+        }
+        catch (InterruptedException e) {}
+      }
+      res.status = id.status;
+    }
+    else if (res.jitter == 0) {
+      res.status = WaitStatus.OK;
+    }
+    else {
+      Debug.log(Debug.DEBUG, "Wait for timestamp " + now + " is late by " + res.jitter + "us");
+      res.status = WaitStatus.LATE;
+    }
+
+    return res;
+  }
+  protected WaitStatus waitAsyncFunc(ClockID id)
+  {
+    return WaitStatus.newOK();
+  }
+  protected void unscheduleFunc(ClockID id)
+  {
+    synchronized (this) {
+      id.status = WaitStatus.UNSCHEDULED;
+      notifyAll();
+    }
+  }
+}
+

Added: trunk/cronus/com/fluendo/jst/WaitStatus.java
===================================================================
--- trunk/cronus/com/fluendo/jst/WaitStatus.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jst/WaitStatus.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,28 @@
+package com.fluendo.jst;
+
+public class WaitStatus {
+  int status;
+  long jitter;
+
+  public static final int OK          =  0;
+  public static final int LATE        =  1;
+  public static final int UNSCHEDULED =  2;
+  public static final int BUSY        =  3;
+  public static final int BADTIME     =  4;
+  public static final int ERROR       =  5;
+  public static final int UNSUPPORTED =  6;
+
+  WaitStatus(int status_, long jitter_) {
+    status = status_;
+    jitter = jitter_;
+  }
+
+  WaitStatus() {
+    status = ERROR;
+    jitter = 0;
+  }
+
+  public static WaitStatus newOK() {
+    return new WaitStatus(OK, 0);
+  }
+}

Added: trunk/cronus/com/fluendo/jtiger/Item.java
===================================================================
--- trunk/cronus/com/fluendo/jtiger/Item.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jtiger/Item.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,217 @@
+/* JTiger
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JTiger are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jtiger;
+
+import java.util.*;
+import java.awt.*;
+import java.awt.image.*;
+import com.fluendo.jkate.Event;
+import com.fluendo.jkate.Tracker;
+import com.fluendo.utils.*;
+
+public class Item {
+  private Tracker kin = null;
+  private boolean alive = false;
+  private Font font = null;
+  private int font_size = 0;
+  private String text = null;
+  private TigerBitmap background_image = null;
+
+  private int width = -1;
+  private int height = -1;
+
+  private float region_x;
+  private float region_y;
+  private float region_w;
+  private float region_h;
+
+  /**
+   * Create a new item from a Kate event.
+   */
+  public Item(com.fluendo.jkate.Event ev) {
+    this.kin = new Tracker(ev);
+    text = null;
+    if (ev.text != null && ev.text.length > 0) {
+      try {
+        text = new String(ev.text, "UTF8");
+      }
+      catch (Exception e) {
+        Debug.warning("Failed to convert text from UTF-8 - text will not display");
+        text = null;
+      }
+    }
+  }
+
+  /**
+   * Create a font suitable for displaying on the given component
+   */
+  protected void createFont(Component c, Image img) {
+    font_size = img.getWidth(null) / 32;
+    if (font_size < 12) font_size = 12;
+    font = new Font("sansserif", Font.BOLD, font_size); // TODO: should be selectable ?
+  }
+
+  /**
+   * Regenerate any cached data to match any relevant changes in the
+   * given component
+   */
+  protected void updateCachedData(Component c, Image img) {
+    int img_width = img.getWidth(null);
+    int img_height = img.getHeight(null);
+
+    if (img_width == width && img_height == height)
+      return;
+
+    createFont(c, img);
+
+    width = img_width;
+    height = img_height;
+  }
+
+  /**
+   * Updates the item at the given time.
+   * returns true for alive, false for dead
+   */
+  public boolean update(Component c, Image img, double t) {
+    com.fluendo.jkate.Event ev = kin.ev;
+    if (ev == null) return false;
+    if (t >= ev.end_time) return false;
+
+    if (t < ev.start_time) {
+      alive = false;
+    }
+    else {
+      alive = true;
+    }
+
+    Dimension d = new Dimension(img.getWidth(null), img.getHeight(null));
+    return kin.update(t-ev.start_time, d, d);
+  }
+
+  /**
+   * Set up the region.
+   */
+  public void setupRegion(Component c, Image img) {
+    if (kin.has[Tracker.has_region]) {
+      region_x = kin.region_x;
+      region_y = kin.region_y;
+      region_w = kin.region_w;
+      region_h = kin.region_h;
+    }
+    else {
+      Dimension d = new Dimension(img.getWidth(null), img.getHeight(null));
+      region_x = d.width * 0.1f;
+      region_y = d.height * 0.8f;
+      region_w = d.width * 0.8f;
+      region_h = d.height * 0.1f;
+    }
+  }
+
+  /**
+   * Renders the item on the given image.
+   */
+  public void render(Component c, Image img) {
+    com.fluendo.jkate.Event ev = kin.ev;
+
+    if (!alive)
+      return;
+
+    updateCachedData(c, img);
+
+    setupRegion(c, img);
+    renderBackground(c, img);
+    renderText(img);
+  }
+
+  /**
+   * Render a background for the item, if approrpiate.
+   * The background may be a color, or an image.
+   */
+  public void renderBackground(Component c, Image img)
+  {
+    if (kin.ev.bitmap != null) {
+      if (background_image == null) {
+        background_image = new TigerBitmap(c, kin.ev.bitmap, kin.ev.palette);
+      }
+      
+      Graphics g = img.getGraphics();
+      int rx = (int)(region_x+0.5), ry = (int)(region_y+0.5);
+      int rw = (int)(region_w+0.5), rh = (int)(region_h+0.5);
+      g.drawImage(background_image.getScaled(rw, rh), rx, ry, null);
+      g.dispose();
+    }
+  }
+
+  /**
+   * Render text text for the item, if approrpiate.
+   */
+  public void renderText(Image img)
+  {
+    if (text == null)
+      return;
+
+    Graphics g = img.getGraphics();
+
+    /* This code uses API calls that were not present in Java 1.1 */
+    /*
+      AttributedString atext = new AttributedString(text, font.getAttributes());
+      AttributedCharacterIterator text_it = atext.getIterator();
+      int text_end = text_it.getEndIndex();
+
+      FontRenderContext frc = g.getFontRenderContext();
+      LineBreakMeasurer lbm = new LineBreakMeasurer(text_it, frc);
+      float dy = 0.0f;
+      float shadow_dx = font_size * 0.05f, shadow_dy = font_size * 0.05f;
+      while (lbm.getPosition() < text_end) {
+        TextLayout layout = lbm.nextLayout(region_w);
+        dy += layout.getAscent();
+        float tw = layout.getAdvance();
+
+        g.setColor(Color.black);
+        layout.draw(g, region_x+((region_w-tw)/2)+shadow_dx, region_y+dy+shadow_dy);
+        layout.draw(g, region_x+((region_w-tw)/2)-shadow_dx, region_y+dy-shadow_dy);
+        layout.draw(g, region_x+((region_w-tw)/2)+shadow_dx, region_y+dy-shadow_dy);
+        layout.draw(g, region_x+((region_w-tw)/2)-shadow_dx, region_y+dy+shadow_dy);
+        g.setColor(Color.white);
+        layout.draw(g, region_x+((region_w-tw)/2), region_y+dy);
+
+        dy += layout.getDescent() + layout.getLeading();
+      }
+    */
+
+    g.setFont(font);
+    FontMetrics fm = g.getFontMetrics();
+    float tw = fm.stringWidth(text);
+    float dy = 0.0f;
+    float shadow_dx = font_size * 0.05f, shadow_dy = font_size * 0.05f;
+
+    g.setColor(Color.black);
+    g.drawString(text, (int)(region_x+((region_w-tw)/2)+shadow_dx+0.5f), (int)(region_y+dy+shadow_dy+0.5f));
+    g.drawString(text, (int)(region_x+((region_w-tw)/2)-shadow_dx+0.5f), (int)(region_y+dy-shadow_dy+0.5f));
+    g.drawString(text, (int)(region_x+((region_w-tw)/2)+shadow_dx+0.5f), (int)(region_y+dy-shadow_dy+0.5f));
+    g.drawString(text, (int)(region_x+((region_w-tw)/2)-shadow_dx+0.5f), (int)(region_y+dy+shadow_dy+0.5f));
+
+    g.setColor(Color.white);
+    g.drawString(text, (int)(region_x+((region_w-tw)/2)+0.5f), (int)(region_y+dy+0.5f));
+
+    g.dispose();
+  }
+}

Added: trunk/cronus/com/fluendo/jtiger/Renderer.java
===================================================================
--- trunk/cronus/com/fluendo/jtiger/Renderer.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jtiger/Renderer.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,71 @@
+/* JTiger
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JTiger are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jtiger;
+
+import java.util.*;
+import java.awt.*;
+import java.awt.image.*;
+
+public class Renderer {
+  private Vector items = new Vector();
+
+  /**
+   * Add a new event to the renderer.
+   */
+  public void add(com.fluendo.jkate.Event ev) {
+    items.addElement(new Item(ev));
+  }
+
+  /**
+   * Update the renderer, and all the events it tracks.
+   * Returns 1 if there is nothing to draw, as an optimization
+   */
+  public int update(Component c, Image img, double t) {
+    for (int n=0; n<items.size(); ++n) {
+      boolean ret = ((Item)items.elementAt(n)).update(c, img, t);
+      if (!ret) {
+        items.removeElementAt(n);
+        --n;
+      }
+    }
+    if (items.size() == 0)
+      return 1;
+    return 0;
+  }
+
+  /**
+   * Renders onto the given image.
+   */
+  public Image render(Component c, Image img) {
+    /* there used to be some non copying code using BufferedImage, but that's not in SDK 1.1, so we do it the slow way */
+    Image copy = c.createImage(img.getWidth(null), img.getHeight(null));
+    Graphics g = copy.getGraphics();
+    g.drawImage(img, 0, 0, null);
+    g.dispose();
+    img = copy;
+
+    for (int n=0; n<items.size(); ++n) {
+      ((Item)items.elementAt(n)).render(c, img);
+    }
+
+    return img;
+  }
+}

Added: trunk/cronus/com/fluendo/jtiger/TigerBitmap.java
===================================================================
--- trunk/cronus/com/fluendo/jtiger/TigerBitmap.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/jtiger/TigerBitmap.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,95 @@
+/* JTiger
+ * Copyright (C) 2008 ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * Parts of JTiger are based on code by Wim Taymans <wim at fluendo.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.fluendo.jtiger;
+
+import java.awt.*;
+import java.awt.image.*;
+import com.fluendo.jkate.*;
+import com.fluendo.utils.*;
+
+public class TigerBitmap {
+  private Image image;
+  private Image scaled_image;
+
+  /**
+   * Create a new TigerBitmap from a Kate bitmap and optional palette.
+   */
+  public TigerBitmap(Component c, Bitmap kb, Palette kp)
+  {
+    if (kb == null) {
+      image = null;
+    }
+    else if (kb.bpp == 0) {
+      /* PNG */
+      image = createPNGBitmap(c, kb, kp);
+    }
+    else {
+      if (kp == null) {
+        image = null;
+      }
+      else {
+        image = createPalettedBitmap(c, kb, kp);
+      }
+    }
+    if (image == null) {
+      // if we failed to create the bitmap, replace it with a 1x1 transparent one to avoid corner cases
+      image = c.getToolkit().createImage(new MemoryImageSource(1, 1, new int[]{0}, 0, 1));
+    }
+  }
+
+  /**
+   * Returns a scaled version of the image.
+   */
+  public Image getScaled(int width, int height)
+  {
+    if (scaled_image == null || width != scaled_image.getWidth(null) || height != scaled_image.getHeight(null)) {
+      scaled_image = image.getScaledInstance(width, height, Image.SCALE_SMOOTH); // TODO: quality setting
+    }
+    return scaled_image;
+  }
+
+  /**
+   * Create an image from bits representing a PNG image.
+   */
+  private Image createPNGBitmap(Component c, Bitmap kb, Palette kp)
+  {
+    Debug.warning("PNG bitmaps not supported yet");
+    return null;
+  }
+
+  /**
+   * Create a paletted image.
+   */
+  private Image createPalettedBitmap(Component c, Bitmap kb, Palette kp)
+  {
+    byte[] cmap = new byte[4*kp.colors.length];
+    for (int n=0; n<kp.colors.length; ++n) {
+      cmap[n*4+0] = kp.colors[n].r;
+      cmap[n*4+1] = kp.colors[n].g;
+      cmap[n*4+2] = kp.colors[n].b;
+      cmap[n*4+3] = kp.colors[n].a;
+    }
+
+    IndexColorModel icm = new IndexColorModel(kb.bpp, kp.colors.length, cmap, 0, true);
+    return c.createImage(new MemoryImageSource(kb.width, kb.height, icm, kb.pixels, 0, kb.width));
+  }
+}
+

Added: trunk/cronus/com/fluendo/player/Configure.java
===================================================================
--- trunk/cronus/com/fluendo/player/Configure.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/player/Configure.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,17 @@
+
+package com.fluendo.player;
+
+class Configure
+{
+  public String buildInfo = "Built on 2009-11-04 07:09:02 GMT (version 0.5.0) in debug mode.";
+
+  public String buildDate = "2009-11-04 07:09:02 GMT";
+  public String buildVersion = "0.5.0";
+  public String buildType = "debug";
+  public String revision = "0.5.0-5-gcc869bc";
+  public String branch = "Xiph";
+
+  public Configure() {
+  }
+}
+    
\ No newline at end of file

Added: trunk/cronus/com/fluendo/player/Cortado.java
===================================================================
--- trunk/cronus/com/fluendo/player/Cortado.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/player/Cortado.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,938 @@
+/* Cortado - a video player java applet
+ * Copyright (C) 2004 Fluendo S.L.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+package com.fluendo.player;
+
+import java.applet.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.net.*;
+import java.util.*;
+import com.fluendo.utils.*;
+import com.fluendo.jst.*;
+
+public class Cortado extends Applet implements Runnable, MouseMotionListener,
+        MouseListener, ComponentListener, BusHandler, StatusListener, ActionListener {
+
+    private static final long serialVersionUID = 1L;
+    private Cortado cortado;
+    private CortadoPipeline pipeline;
+    private String urlString;
+    private boolean audio;
+    private boolean video;
+    private int kateIndex;
+    private String kateLanguage;
+    private String kateCategory;
+    private boolean showSpeaker;
+    private boolean keepAspect;
+    private boolean ignoreAspect;
+    private boolean autoPlay;
+    private int bufferSize;
+    private String userId;
+    private String password;
+    private int bufferLow;
+    private int bufferHigh;
+    private int debug;
+    private double durationParam;
+    private boolean statusRunning;
+    private Thread statusThread;
+    public Status status;
+    private int statusHeight = 20;
+    private boolean inStatus;
+    private boolean isBuffering;
+    private int desiredState;
+    private boolean started = false;
+    private boolean isEOS;
+    private boolean isError;
+    private static final String[] autoBoolVals = {"auto", "true", "false"};
+    private static final int BOOL_AUTO = 0;
+    private static final int BOOL_TRUE = 1;
+    private static final int BOOL_FALSE = 2;
+    private int seekable;
+    private int live;
+    private int showStatus;
+    private static final String[] showStatusVals = {"auto", "show", "hide"};
+    public static final int STATUS_AUTO = 0;
+    public static final int STATUS_SHOW = 1;
+    public static final int STATUS_HIDE = 2;
+    private int hideTimeout;
+    private int hideCounter;
+    private boolean mayHide;
+
+    // HTML-5 media element attributes
+    public double currentTime = 0;
+    private double _currentTime; // last computed time to detect seek request
+    public double duration = -1;
+    public boolean paused;
+    public String src;
+    
+    private PopupMenu menu;
+    private Hashtable params = new Hashtable();
+    private Configure configure;
+    private Dimension appletDimension;
+			
+			public Cortado() {
+				
+			}
+    public String getAppletInfo() {
+        return "Title: Fluendo media player \nAuthor: Wim Taymans \nA Java based network multimedia player.";
+    }
+
+    public String getRevision() {
+        return "$Revision$";
+    }
+
+    public String[][] getParameterInfo() {
+        String[][] info = {
+            {"url", "URL", "The media file to play"},
+            {"seekable", "enum",
+                "Can you seek in this file (auto|true|false) (default auto)"},
+            {"live", "enum",
+                "Is this a live stream (disabled PAUSE) (auto|true|false) (default auto)"},
+            {"duration", "float",
+                "Total duration of the file in seconds (default unknown)"},
+            {"audio", "boolean", "Enable audio playback (default true)"},
+            {"video", "boolean", "Enable video playback (default true)"},
+            {"kateIndex", "boolean", "Enable playback of a particular Kate stream (default -1 (none))"},
+            {"kateLanguage", "string", "Enable playback of a Kate stream from a language (default empty)"},
+            {"kateCategory", "string", "Enable playback of a Kate stream from a category (default empty)"},
+            {"statusHeight", "int", "The height of the status area (default 12)"},
+            {"autoPlay", "boolean", "Automatically start playback (default true)"},
+            {"showStatus", "enum", "Show status area (auto|show|hide) (default auto)"},
+            {"hideTimeout", "int", "Timeout in seconds to hide the status area when " +
+                "showStatus is auto (default 0)"},
+            {"showSpeaker", "boolean", "Show a speaker icon when audio is available (default true)"},
+            {"keepAspect", "boolean",
+                "Use aspect ratio of video (default true)"},
+            {"ignoreAspect", "boolean",
+                "Ignore the aspect ratio as signalled by the video, always assume square pixels (default false)"},
+            {"bufferSize", "int",
+                "The size of the prebuffer in Kbytes (default 100)"},
+            {"bufferLow", "int", "Percent of empty buffer (default 10)"},
+            {"bufferHigh", "int", "Percent of full buffer (default 70)"},
+            {"userId", "string",
+                "userId for basic authentication (default null)"},
+            {"password", "string",
+                "password for basic authentication (default null)"},
+            {"debug", "int", "Debug level 0 - 4 (default = 3)"},};
+        return info;
+    }
+
+    public void setParam(String name, String value) {
+        params.put(name, value);
+    }
+
+    public void restart() {
+        stop();
+        init();
+        start();
+    }
+
+    public String getParam(String name, String def) {
+        String result;
+
+        result = (String) params.get(name);
+
+        if (result == null) {
+            try {
+                result = getParameter(name);
+            } catch (Exception e) {
+            }
+        }
+        if (result == null) {
+            result = def;
+        }
+        return result;
+    }
+
+    public int getEnumParam(String name, String[] vals, String def) {
+        int res = -1;
+
+        String val = getParam(name, def);
+        for (int i = 0; i < vals.length; i++) {
+            if (vals[i].equals(val)) {
+                res = i;
+                break;
+            }
+        }
+        if (res != -1) {
+            Debug.info("param \"" + name + "\" has enum value \"" + res + "\" (" + vals[res] + ")");
+        } else {
+            Debug.info("param \"" + name + "\" has invalid enum value");
+        }
+        return res;
+    }
+
+    public String getStringParam(String name, String def) {
+        String res = getParam(name, def);
+        Debug.info("param \"" + name + "\" has string value \"" + res + "\"");
+        return res;
+    }
+
+    public boolean getBoolParam(String name, boolean def) {
+        boolean res;
+        String defStr = def ? "true" : "false";
+        String paramVal;
+
+        paramVal = String.valueOf(getParam(name, defStr));
+
+        res = paramVal.equals("true");
+        res |= paramVal.equals("1");
+
+        Debug.info("param \"" + name + "\" has boolean value \"" + res + "\"");
+        return res;
+    }
+
+    public double getDoubleParam(String name, double def) {
+        double res;
+        res = Double.valueOf(getParam(name, "" + def)).doubleValue();
+        Debug.info("param \"" + name + "\" has double value \"" + res + "\"");
+        return res;
+    }
+
+    public int getIntParam(String name, int def) {
+        int res;
+        res = Integer.valueOf(getParam(name, "" + def)).intValue();
+        Debug.info("param \"" + name + "\" has int value \"" + res + "\"");
+        return res;
+    }
+
+    public void shutDown(Throwable error) {
+        Debug.log(Debug.INFO, "shutting down: reason: " + error.getMessage());
+        error.printStackTrace();
+        stop();
+    }
+
+    public synchronized void init() {
+        cortado = this;
+
+        Debug.info("init()");
+
+        if (pipeline != null) {
+            stop();
+        }
+
+        pipeline = new CortadoPipeline(this);
+        configure = new Configure();
+
+        src = urlString = getStringParam("url", null);
+		seekable = getEnumParam("seekable", autoBoolVals, "auto");
+		durationParam = getDoubleParam("duration", -1.0);
+        live = getEnumParam("live", autoBoolVals, "auto");
+        audio = getBoolParam("audio", true);
+        video = getBoolParam("video", true);
+        kateIndex = getIntParam("kateIndex", -1);
+        kateLanguage = getStringParam("kateLanguage", "");
+        kateCategory = getStringParam("kateCategory", "");
+        statusHeight = getIntParam("statusHeight", 12);
+        autoPlay = getBoolParam("autoPlay", true);
+        showStatus = getEnumParam("showStatus", showStatusVals, "auto");
+        hideTimeout = getIntParam("hideTimeout", 3);
+        showSpeaker = getBoolParam("showSpeaker", true);
+        keepAspect = getBoolParam("keepAspect", true);
+        ignoreAspect = getBoolParam("ignoreAspect", false);
+        bufferSize = getIntParam("bufferSize", 200);
+        bufferLow = getIntParam("bufferLow", 10);
+        bufferHigh = getIntParam("bufferHigh", 70);
+        debug = getIntParam("debug", 3);
+        userId = getStringParam("userId", null);
+        password = getStringParam("password", null);
+
+        // if audio-only don't hide the status bar
+        if (!video) {
+            hideTimeout = Integer.MAX_VALUE;
+        }
+
+        Debug.level = debug;
+        Debug.log(Debug.INFO, "build info: " + configure.buildInfo);
+        Debug.log(Debug.INFO, "revision: " + getRevision());
+
+        /* FIXME, HTTP Range returns 206, which HTTPConnection on MS JVM thinks
+         * is a fatal error. Disable seeking for now. */
+        if (System.getProperty("java.vendor").toUpperCase().startsWith("MICROSOFT", 0)) {
+            Debug.log(Debug.WARNING, "Found MS JVM, disable seeking.");
+            seekable = BOOL_FALSE;
+        }
+
+        pipeline.setUrl(urlString);
+        pipeline.setUserId(userId);
+        pipeline.setPassword(password);
+        pipeline.enableAudio(audio);
+        pipeline.enableVideo(video);
+        pipeline.setIgnoreAspect(ignoreAspect);
+        pipeline.enableKateStream(kateIndex, kateLanguage, kateCategory);
+        pipeline.setBufferSize(bufferSize);
+        pipeline.setBufferLow(bufferLow);
+        pipeline.setBufferHigh(bufferHigh);
+
+        URL documentBase;
+        try {
+            documentBase = getDocumentBase();
+            Debug.log(Debug.INFO, "Document base: " + documentBase);
+        } catch (Throwable t) {
+            documentBase = null;
+        }
+        pipeline.setDocumentBase(documentBase);
+        pipeline.setComponent(this);
+        pipeline.getBus().addHandler(this);
+
+        setBackground(Color.black);
+        setForeground(Color.white);
+
+        status = new Status(this);
+        status.setShowSpeaker(showSpeaker);
+        status.setHaveAudio(audio);
+        status.setHavePercent(true);
+        /* assume live stream unless specified */
+        if (live == BOOL_FALSE) {
+            status.setLive(false);
+        } else {
+            status.setLive(true);
+        }
+
+        /* assume non seekable stream unless specified */
+        if (seekable == BOOL_TRUE) {
+            status.setSeekable(true);
+        } else {
+            status.setSeekable(false);
+        }
+
+        if (durationParam < 0) {
+            try {
+                String base = documentBase != null ? documentBase.toString().substring(0, documentBase.toString().lastIndexOf("/")) : "";
+                String docurlstring = (urlString.indexOf("://") >= 0) ? urlString : base + "/" + urlString;
+                Debug.log(Debug.INFO, "trying to determine duration for " + docurlstring);
+                URL url = new URL(docurlstring);
+                duration = durationParam = new DurationScanner().getDurationForURL(url, userId, password);
+                Debug.log(Debug.INFO, "Determined stream duration to be approx. " + durationParam);
+            } catch (Exception ex) {
+                Debug.log(Debug.WARNING, "Couldn't determine duration for stream.");
+            }
+        }
+
+        status.setDuration(durationParam);
+        inStatus = false;
+        mayHide = (hideTimeout == 0);
+        hideCounter = 0;
+        if (showStatus != STATUS_HIDE) {
+            status.setVisible(true);
+        } else {
+            status.setVisible(false);
+        }
+
+        menu = new PopupMenu();
+        menu.add("About...");
+        menu.addActionListener(this);
+        this.add(menu);
+    }
+
+    public void actionPerformed(ActionEvent e) {
+        String command = e.getActionCommand();
+
+        if (command.equals("About...")) {
+            AboutFrame about = new AboutFrame(pipeline);
+            about.d.setVisible(true);
+        }
+    }
+
+    public Graphics getGraphics() {
+        Dimension dim = getSize();
+        Graphics g = super.getGraphics();
+
+        if (status != null && status.isVisible()) {
+            g.setClip(0, 0, dim.width, dim.height - statusHeight);
+        } else {
+            g.setClip(0, 0, dim.width, dim.height);
+        }
+        return g;
+    }
+
+    public void componentHidden(ComponentEvent e) {
+    }
+
+    public void componentMoved(ComponentEvent e) {
+    }
+
+    public void componentResized(ComponentEvent e) {
+        /* reset cached dimension */
+        appletDimension = super.getSize();
+        if (pipeline != null) {
+            pipeline.resize(appletDimension);
+        }
+    }
+
+    public void componentShown(ComponentEvent e) {
+        // reset cached dimension
+        appletDimension = super.getSize();
+        Debug.debug("Component shown, size = " + appletDimension);
+        if (pipeline != null) {
+            pipeline.resize(appletDimension);
+        }
+    }
+
+    public Dimension getSize() {
+        if (appletDimension == null) {
+            appletDimension = super.getSize();
+        }
+
+        return appletDimension;
+    }
+
+    public void update(Graphics g) {
+        paint(g);
+    }
+
+    public void run() {
+        try {
+            realRun();
+        } catch (Throwable t) {
+            shutDown(t);
+        }
+    }
+
+    private void realRun() {
+        Debug.log(Debug.INFO, "entering status thread");
+        while (statusRunning) {
+            try {
+                long now;
+
+                if (_currentTime != currentTime && currentTime >= 0 && duration > currentTime) {
+                    doSeek(currentTime / duration);
+                }
+                currentTime = _currentTime = (double) pipeline.getPosition() / Clock.SECOND;
+
+                now = pipeline.getPosition() / Clock.SECOND;
+                status.setTime(now);
+
+                Thread.sleep(1000);
+
+                if (hideCounter > 0) {
+                    hideCounter--;
+                    if (hideCounter == 0) {
+                        mayHide = true;
+                        setStatusVisible(false, false);
+                    }
+                }
+
+                // we want to be able to change subtitles at runtime without having
+                // to restart the player, and selecting a stream which is already selected
+                // is very cheap, so we update the stream to play periodically.
+                pipeline.enableKateStream(
+                  Integer.valueOf(getParam("kateIndex", "" + -1)).intValue(),
+                  getParam("kateLanguage", ""),
+                  getParam("kateCategory", "")
+                );
+
+            } catch (Exception e) {
+                if (statusRunning) {
+                    Debug.log(Debug.ERROR, "Exception in status thread:");
+                    e.printStackTrace();
+                }
+            }
+        }
+        Debug.log(Debug.INFO, "exit status thread");
+    }
+
+    public void paint(Graphics g) {
+        Dimension dim = getSize();
+
+        int dwidth = dim.width;
+        int dheight = dim.height;
+
+        /* sometimes dimension is wrong */
+        if (dwidth <= 0 || dheight < statusHeight) {
+            appletDimension = null;
+            Debug.log(Debug.WARNING,
+                    "paint aborted: appletDimension wrong; dwidth " + dwidth + ", dheight " + dheight + ", statusHeight " + statusHeight);
+            return;
+        }
+
+        if (status != null) {
+            status.setBounds(0, dheight - statusHeight, dwidth, statusHeight);
+            status.paint(g);
+        }
+    }
+
+    private void setStatusVisible(boolean b, boolean force) {
+        /* no change, do nothing */
+        if (status.isVisible() == b) {
+            return;
+        }
+
+        /* refuse to hide when hideTimeout did not expire */
+        if (!b && !mayHide) {
+            return;
+        }
+
+        if (!force) {
+            if (showStatus == STATUS_SHOW && !b) {
+                return;
+            }
+            if (showStatus == STATUS_HIDE && b) {
+                return;
+            }
+        }
+        /* never hide when we are in error */
+        if (isError && !b) {
+            return;
+        }
+
+        /* don't make invisible when the mouse pointer is inside status area */
+        if (inStatus && !b) {
+            b = true;
+        }
+
+        if (b != status.isVisible()) {
+            Debug.log(Debug.INFO, "Status: " + (b ? "Show" : "Hide"));
+        }
+
+        if (b) {
+            hideCounter = hideTimeout;
+        }
+        status.setVisible(b);
+        repaint();
+    }
+
+    private boolean intersectStatus(MouseEvent e) {
+        int y = e.getY();
+        int max = getSize().height;
+        int top = max - statusHeight;
+
+        inStatus = y > top && y < max;
+        return inStatus;
+    }
+
+    public void mouseClicked(MouseEvent e) {
+    }
+
+    public void mouseEntered(MouseEvent e) {
+    }
+
+    public void mouseExited(MouseEvent e) {
+        inStatus = false;
+    }
+
+    public void mousePressed(MouseEvent e) {
+        if (intersectStatus(e)) {
+            int y = getSize().height - statusHeight;
+            e.translatePoint(0, -y);
+            ((MouseListener) status).mousePressed(e);
+        } else {
+            if ((e.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK) {
+                menu.show(this, e.getX(), e.getY());
+            }
+        }
+    }
+
+    public void mouseReleased(MouseEvent e) {
+        if (intersectStatus(e)) {
+            int y = getSize().height - statusHeight;
+            e.translatePoint(0, -y);
+            ((MouseListener) status).mouseReleased(e);
+        } else {
+            status.cancelMouseOperation();
+        }
+    }
+
+    public void mouseDragged(MouseEvent e) {
+        if (intersectStatus(e)) {
+            int y = getSize().height - statusHeight;
+            setStatusVisible(true, false);
+            e.translatePoint(0, -y);
+            ((MouseMotionListener) status).mouseDragged(e);
+        } /*else {
+    setStatusVisible(false, false);
+    }*/
+    }
+
+    public void mouseMoved(MouseEvent e) {
+        if (intersectStatus(e)) {
+            int y = getSize().height - statusHeight;
+            setStatusVisible(true, false);
+            e.translatePoint(0, -y);
+            ((MouseMotionListener) status).mouseMoved(e);
+        } /*else {
+        setStatusVisible(false, false);
+        }*/
+        setStatusVisible(true, false);
+    }
+
+    public void handleMessage(Message msg) {
+        switch (msg.getType()) {
+            case Message.WARNING:
+                Debug.info(msg.toString());
+                break;
+            case Message.ERROR:
+                Debug.info(msg.toString());
+                if (!isError) {
+                    status.setMessage(msg.parseErrorString());
+                    status.setState(Status.STATE_STOPPED);
+                    pipeline.setState(Element.STOP);
+                    setStatusVisible(true, true);
+                    isError = true;
+                }
+                break;
+            case Message.EOS:
+                Debug.log(Debug.INFO, "EOS: playback ended");
+                if (!isError) {
+                    status.setState(Status.STATE_STOPPED);
+                    status.setMessage("Playback ended");
+                    isEOS = true;
+                    pipeline.setState(Element.STOP);
+                    setStatusVisible(true, false);
+                }
+                break;
+            case Message.STREAM_STATUS:
+                Debug.info(msg.toString());
+                break;
+            case Message.RESOURCE:
+                if (!isError) {
+                    status.setMessage(msg.parseResourceString());
+                    setStatusVisible(true, false);
+                }
+                break;
+            case Message.DURATION:
+                long duration;
+
+                duration = msg.parseDurationValue();
+
+                status.setByteDuration(duration);
+
+                Debug.log(Debug.DEBUG, "got duration: " + duration);
+                if (duration != -1) {
+                    /* we got duration, we can enable automatic setting */
+					if (seekable == BOOL_AUTO) {
+                        status.setSeekable(true);
+                    }
+                    if (live == BOOL_AUTO) {
+                        status.setLive(false);
+                    }
+                }
+                break;
+            case Message.BUFFERING:
+                boolean busy;
+                int percent;
+
+                if (isError) {
+                    break;
+                }
+
+                busy = msg.parseBufferingBusy();
+                percent = msg.parseBufferingPercent();
+
+                if (busy) {
+                    if (!isBuffering) {
+                        Debug.log(Debug.INFO, "PAUSE: we are buffering");
+                        if (desiredState == Element.PLAY) {
+                            pipeline.setState(Element.PAUSE);
+                        }
+                        isBuffering = true;
+                        setStatusVisible(true, false);
+                    }
+                    status.setBufferPercent(busy, percent);
+                } else {
+                    if (isBuffering) {
+                        Debug.log(Debug.INFO, "PLAY: we finished buffering");
+                        if (desiredState == Element.PLAY) {
+                            pipeline.setState(Element.PLAY);
+                        }
+                        isBuffering = false;
+                        setStatusVisible(false, false);
+                    }
+                    status.setBufferPercent(busy, percent);
+                }
+                break;
+            case Message.STATE_CHANGED:
+                if (msg.getSrc() == pipeline) {
+                    int old, next;
+
+                    old = msg.parseStateChangedOld();
+                    next = msg.parseStateChangedNext();
+
+                    switch (next) {
+                        case Element.PAUSE:
+                            if (!isError && !isEOS) {
+                                status.setMessage("Paused");
+                            }
+                            status.setState(Status.STATE_PAUSED);
+                            break;
+                        case Element.PLAY:
+                            if (!isError && !isEOS) {
+                                status.setMessage("Playing");
+                                setStatusVisible(false, false);
+                                if (!mayHide) {
+                                    hideCounter = hideTimeout;
+                                }
+                            }
+                            status.setState(Status.STATE_PLAYING);
+                            break;
+                        case Element.STOP:
+                            if (!isError && !isEOS) {
+                                status.setMessage("Stopped");
+                                setStatusVisible(true, false);
+                            }
+                            status.setState(Status.STATE_STOPPED);
+                            break;
+                    }
+                }
+                break;
+            case Message.BYTEPOSITION:
+                status.setBytePosition(msg.parseBytePosition());
+                break;
+            default:
+                break;
+        }
+    }
+
+    public void doPause() {
+        isError = false;
+        isEOS = false;
+        paused = true;
+        status.setMessage("Pause");
+        desiredState = Element.PAUSE;
+        pipeline.setState(desiredState);
+    }
+
+    public void doPlay() {
+        isError = false;
+        isEOS = false;
+        paused = false;
+        status.setMessage("Play");
+        desiredState = Element.PLAY;
+        pipeline.setState(desiredState);
+    }
+
+    public void doStop() {
+        status.setMessage("Stop");
+        desiredState = Element.STOP;
+        pipeline.setState(desiredState);
+    }
+
+    public void doSeek(double aPos) {
+        boolean res;
+        com.fluendo.jst.Event event;
+
+        /* get value, convert to PERCENT and construct seek event */
+        event = com.fluendo.jst.Event.newSeek(Format.PERCENT,
+                (int) (aPos * 100.0 * Format.PERCENT_SCALE));
+		
+        /* send event to pipeline */
+        res = pipeline.sendEvent(event);
+        if (!res) {
+            Debug.log(Debug.WARNING, "seek failed");
+        }
+    }
+
+    public double getPlayPosition() {
+        return (double) pipeline.getPosition() / Clock.SECOND;
+    }
+
+    public void newState(int aState) {
+        int ret;
+        switch (aState) {
+            case Status.STATE_PAUSED:
+                doPause();
+                break;
+            case Status.STATE_PLAYING:
+                doPlay();
+                break;
+            case Status.STATE_STOPPED:
+                doStop();
+                break;
+            default:
+                break;
+        }
+    }
+
+    public void newSeek(double aPos) {
+        doSeek(aPos);
+    }
+
+    public synchronized void start() {
+        int res;
+
+        Debug.info("Application starting");
+
+        addComponentListener(this);
+        addMouseListener(this);
+        addMouseMotionListener(this);
+        status.addStatusListener(this);
+
+        if (autoPlay) {
+            desiredState = Element.PLAY;
+        } else {
+            desiredState = Element.PAUSE;
+        }
+
+        res = pipeline.setState(desiredState);
+
+        if (statusThread != null) {
+            throw new RuntimeException("invalid state");
+        }
+
+        statusThread = new Thread(this, "cortado-StatusThread-" + Debug.genId());
+        statusRunning = true;
+        statusThread.start();
+    }
+
+    public synchronized void stop() {
+        Debug.info("Application stopping...");
+
+        status.removeStatusListener(this);
+        removeMouseMotionListener(this);
+        removeMouseListener(this);
+        removeComponentListener(this);
+
+        statusRunning = false;
+        desiredState = Element.STOP;
+        if (pipeline != null) {
+            try {
+                pipeline.setState(desiredState);
+            } catch (Throwable e) {
+            }
+            try {
+                pipeline.shutDown();
+            } catch (Throwable e) {
+            }
+            pipeline = null;
+        }
+        if (statusThread != null) {
+            try {
+                statusThread.interrupt();
+            } catch (Throwable e) {
+            }
+            try {
+                statusThread.join();
+            } catch (Throwable e) {
+            }
+            statusThread = null;
+        }
+        Debug.info("Application stopped");
+    }
+
+    public int getStatusHeight() {
+        return statusHeight;
+    }
+
+    public int getShowStatus() {
+        return showStatus;
+    }
+
+    /* HTML-5 media element methods */
+    public synchronized void play() {
+        doPlay();
+    }
+
+    public synchronized void pause() {
+        doPause();
+    }
+}
+
+/* dialog box */
+class AppFrame extends Frame
+        implements WindowListener {
+
+    public AppFrame(String title) {
+        super(title);
+        addWindowListener(this);
+    }
+
+    public void windowClosing(WindowEvent e) {
+        setVisible(false);
+        dispose();
+        System.exit(0);
+    }
+
+    public void windowClosed(WindowEvent e) {
+    }
+
+    public void windowDeactivated(WindowEvent e) {
+    }
+
+    public void windowActivated(WindowEvent e) {
+    }
+
+    public void windowDeiconified(WindowEvent e) {
+    }
+
+    public void windowIconified(WindowEvent e) {
+    }
+
+    public void windowOpened(WindowEvent e) {
+    }
+}
+
+class AboutFrame extends AppFrame {
+
+    Dialog d;
+
+    public String getRevision() {
+        return "$Revision$";
+    }
+
+    public AboutFrame(CortadoPipeline pipeline) {
+        super("AboutFrame");
+
+        Configure configure = new Configure();
+        SourceInfo info = new SourceInfo();
+
+        setSize(200, 100);
+        Button dbtn;
+        d = new Dialog(this, "About Cortado", false);
+        d.setVisible(true);
+
+        TextArea ta = new TextArea("", 10, 46, TextArea.SCROLLBARS_NONE);
+        d.add(ta);
+        ta.appendText("This is Cronus " + configure.buildVersion + ".\n");
+		ta.appendText("Brought to you by Michael Scheerer,\n");
+		ta.appendText("based on the Jst framework brought to you by Wim Taymans.\n");
+        ta.appendText("(C) Copyright 2004,2005,2006 Fluendo,\n(C) Copyright 2009, 2010 Meviatronic.\n\n");
+        ta.appendText("Built on " + configure.buildDate + "\n");
+        ta.appendText("Built in " + configure.buildType + " mode.\n");
+        ta.appendText("Built from git branch " + info.branch + ", revision " +
+                info.revision + "\n");
+        ta.appendText("Running on Java VM " + System.getProperty("java.version") + " from " + System.getProperty("java.vendor") + "\n");
+
+        if (pipeline.isAudioEnabled()) {
+            if (pipeline.usingJavaX) {
+                ta.appendText("Using the javax.sound backend.");
+            } else {
+                ta.appendText("Using the sun.audio backend.\n\n");
+                ta.appendText("NOTE: you should install the Java(TM) from Sun for better audio quality.");
+            }
+        }
+		
+        d.add(dbtn = new Button("OK"),
+                BorderLayout.SOUTH);
+
+        Dimension dim = d.getPreferredSize();
+        dim.height += 30;
+        d.setSize(dim);
+        dbtn.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+                d.setVisible(false);
+            }
+        });
+        d.addWindowListener(new WindowAdapter() {
+
+            public void windowClosing(WindowEvent e) {
+                d.setVisible(false);
+            }
+        });
+    }
+}

Added: trunk/cronus/com/fluendo/player/CortadoPipeline.java
===================================================================
--- trunk/cronus/com/fluendo/player/CortadoPipeline.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/player/CortadoPipeline.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,768 @@
+/* Cortado - a video player java applet
+ * Copyright (C) 2005 Fluendo S.L.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.player;
+
+import java.util.*;
+import java.awt.*;
+import java.net.URL;
+
+import com.fluendo.jst.*;
+import com.fluendo.utils.*;
+
+public class CortadoPipeline extends Pipeline implements PadListener, CapsListener {
+
+  private String url;
+  private String userId;
+  private String password;
+  private boolean enableAudio;
+  private boolean enableVideo;
+  private boolean ignoreAspect;
+  private int enableKate;
+  private Component component;
+  private int bufferSize = -1;
+  private int bufferLow = -1;
+  private int bufferHigh = -1;
+  private URL documentBase = null;
+  private Cortado application;
+	
+  private Element httpsrc;
+  private Element buffer;
+  private Element demux;
+  private Element videodec;
+  private Element audiodec;
+  private Element videosink;
+  private Element audiosink;
+  private Element v_queue, v_queue2, a_queue;
+  private Element overlay;
+  private Pad asinkpad, ovsinkpad, oksinkpad;
+  private Pad apad, vpad;
+  private Vector katedec = new Vector();
+  private Vector k_queue = new Vector();
+  private Element kselector = null;
+
+  public boolean usingJavaX = false;
+
+	private boolean setupVideoDec (String name) {
+    videodec = ElementFactory.makeByName(name, "videodec");
+    if (videodec == null) {
+      noSuchElement (name);
+      return false;
+    }
+    add(videodec);
+    return true;
+  }
+
+  public void padAdded(Pad pad) {
+    Caps caps = pad.getCaps ();
+    Pad tmp;
+
+    if (caps == null) {
+      Debug.log(Debug.INFO, "pad added without caps: "+pad);
+      return;
+    }
+    Debug.log(Debug.INFO, "pad added "+pad);
+
+    String mime = caps.getMime();
+    
+    if (enableAudio && mime.equals("audio/x-vorbis")) {
+      a_queue = ElementFactory.makeByName("queue", "a_queue");
+      if (a_queue == null) {
+        noSuchElement ("queue");
+        return;
+      }
+
+      audiodec = ElementFactory.makeByName("vorbisdec", "audiodec");
+      if (audiodec == null) {
+        noSuchElement ("vorbisdec");
+        return;
+      }
+
+      add(a_queue);
+      add(audiodec);
+
+      pad.link(a_queue.getPad("sink"));
+      a_queue.getPad("src").link(audiodec.getPad("sink"));
+      if (!audiodec.getPad("src").link(asinkpad)) {
+        postMessage (Message.newError (this, "audiosink already linked"));
+        return;
+      }
+
+      apad = pad;
+
+      audiodec.setState (PAUSE);
+	  a_queue.setState (PAUSE);
+    }
+	  else if (enableVideo && mime.equals("video/x-theora")) {
+      // Constructs a chain of the form
+      // oggdemux -> v_queue -> theoradec -> v_queue2 -> videosink
+      v_queue = ElementFactory.makeByName("queue", "v_queue");
+      v_queue2 = ElementFactory.makeByName("queue", "v_queue2");
+      if (v_queue == null) {
+        noSuchElement ("queue");
+	    return;
+      }
+
+      if (!setupVideoDec ("theoradec"))
+        return;
+
+      v_queue2.setProperty("maxBuffers","1");
+
+      add(v_queue);
+      add(v_queue2);
+
+      pad.link(v_queue.getPad("sink"));
+      v_queue.getPad("src").link(videodec.getPad("sink"));
+      videodec.getPad("src").link(v_queue2.getPad("sink"));
+      if (!v_queue2.getPad("src").link(ovsinkpad)) {
+        postMessage (Message.newError (this, "videosink already linked"));
+        return;
+      }
+
+      vpad = pad;
+
+      videodec.setState (PAUSE);
+      v_queue.setState (PAUSE);
+      v_queue2.setState (PAUSE);
+    }
+    else if (enableVideo && mime.equals("image/jpeg")) {
+      if (!setupVideoDec ("jpegdec")) {
+        return;
+      }
+      videodec.setProperty ("component", component);
+      
+      pad.link(videodec.getPad("sink"));
+      if (!videodec.getPad("src").link(ovsinkpad)) {
+        postMessage (Message.newError (this, "videosink already linked"));
+        return;
+      }
+
+      videodec.setState (PAUSE);
+    }
+    else if (enableVideo && mime.equals("video/x-smoke")) {
+      if (!setupVideoDec ("smokedec")) {
+        return;
+      }
+      videodec.setProperty ("component", component);
+      
+      pad.link(videodec.getPad("sink"));
+      if (!videodec.getPad("src").link(ovsinkpad)) {
+        postMessage (Message.newError (this, "videosink already linked"));
+        return;
+      }
+      vpad = pad;
+
+      videodec.setState (PAUSE);
+    }
+    else if (enableVideo && mime.equals("application/x-kate")) {
+      Element tmp_k_queue, tmp_katedec, tmp_katesink;
+      Pad tmp_kpad, tmp_ksinkpad;
+      int kate_index = katedec.size();
+
+      Debug.debug("Found Kate stream, setting up pipeline branch");
+
+      /* dynamically create a queue/decoder/overlay pipeline */
+      tmp_k_queue = ElementFactory.makeByName("queue", "k_queue"+kate_index);
+      if (tmp_k_queue == null) {
+        noSuchElement ("queue");
+        return;
+      }
+
+      tmp_katedec = ElementFactory.makeByName("katedec", "katedec"+kate_index);
+      if (tmp_katedec == null) {
+        noSuchElement ("katedec");
+        return;
+      }
+
+      /* The selector is created when the first Kate stream is encountered */
+      if (kselector == null) {
+        Debug.debug("No Kate selector yet, creating one");
+
+        /* insert an overlay before the video sink */
+        ovsinkpad.unlink();
+        videodec.getPad("src").unlink();
+        overlay = ElementFactory.makeByName("kateoverlay", "overlay");
+        if (overlay == null) {
+          noSuchElement ("overlay");
+          return;
+        }
+        ovsinkpad = overlay.getPad("videosink");
+        oksinkpad = overlay.getPad("katesink");
+        if (!videodec.getPad("src").link(ovsinkpad)) {
+          postMessage (Message.newError (this, "Failed linking video decoder to overlay"));
+          return;
+        }
+        add(overlay);
+        overlay.setProperty ("component", component);
+        overlay.getPad("videosrc").link(videosink.getPad("sink"));
+		  
+        kselector = ElementFactory.makeByName("selector", "selector");
+        if (kselector == null) {
+          noSuchElement ("selector");
+          return;
+        }
+        add(kselector);
+        if (!kselector.getPad("src").link(oksinkpad)) {
+          postMessage (Message.newError (this, "Failed linking Kate selector to overlay"));
+          return;
+        }
+        kselector.setState (PAUSE);
+      }
+      tmp_katesink = kselector;
+
+      add(tmp_k_queue);
+      add(tmp_katedec);
+
+      tmp_kpad = pad;
+      tmp_ksinkpad = tmp_katesink.getPad("sink");
+
+      /* link new elements together */
+      if (!pad.link(tmp_k_queue.getPad("sink"))) {
+        postMessage (Message.newError (this, "Failed to link new Kate stream to queue"));
+        return;
+      }
+      if (!tmp_k_queue.getPad("src").link(tmp_katedec.getPad("sink"))) {
+        postMessage (Message.newError (this, "Failed to link new Kate queue to decoder"));
+        return;
+      }
+
+      Pad new_selector_pad = kselector.requestSinkPad(tmp_katedec.getPad("src"));
+      if (!tmp_katedec.getPad("src").link(new_selector_pad)) {
+        postMessage (Message.newError (this, "kate sink already linked"));
+        return;
+      }
+
+      tmp_katedec.setState (PAUSE);
+      tmp_k_queue.setState (PAUSE);
+
+      /* add to the lists */
+      katedec.addElement(tmp_katedec);
+      k_queue.addElement(tmp_k_queue);
+		
+      /* if we have just added the one that was selected, link it now */
+      if (enableKate == katedec.size()-1) {
+        doEnableKateIndex(enableKate);
+      }
+    }
+  }
+  
+  public void padRemoved(Pad pad) {
+    pad.unlink();
+    if (pad == vpad) {
+      Debug.log(Debug.INFO, "video pad removed "+pad);
+      ovsinkpad.unlink();
+      //oksinkpad.unlink(); // needed ????
+      vpad = null;
+    }
+    else if (pad == apad) {
+      Debug.log(Debug.INFO, "audio pad removed "+pad);
+      asinkpad.unlink();
+      apad = null;
+    }
+  }
+
+  public void noMorePads() {
+    boolean changed = false;
+    Element el;
+
+    Debug.log(Debug.INFO, "all streams detected");
+
+    if (apad == null && enableAudio) {
+      Debug.log(Debug.INFO, "file has no audio, remove audiosink");
+      audiosink.setState(STOP);
+      remove (audiosink);
+      audiosink = null;
+      changed = true;
+    }
+    if (vpad == null && enableVideo) {
+      Debug.log(Debug.INFO, "file has no video, remove videosink");
+      videosink.setState(STOP);
+      if (overlay != null) {
+        overlay.setState(STOP);
+      }
+      for (int n=0; n<katedec.size(); ++n) {
+        el = (Element)katedec.elementAt(n);
+        el.setState(STOP);
+        remove(el);
+        el = (Element)k_queue.elementAt(n);
+        el.setState(STOP);
+        remove(el);
+      }
+      if (kselector != null) {
+        kselector.setState(STOP);
+        remove(kselector);
+        kselector = null;
+      }
+      remove (videosink);
+      remove (overlay);
+      katedec.removeAllElements();
+      k_queue.removeAllElements();
+      videosink = null;
+      overlay = null;
+      changed = true;
+    }
+    if (changed)
+      scheduleReCalcState();
+  }
+
+  public CortadoPipeline (Cortado cortado)
+  {
+    super("pipeline");
+    enableAudio = true;
+    enableVideo = true;
+    application = cortado;
+    enableKate = -1; /* none by default */
+  }
+
+  public void setUrl(String anUrl) {
+    url = anUrl;
+  }
+  public String getUrl() {
+    return url;
+  }
+  public void setUserId(String aUserId) {
+    userId = aUserId;
+  }
+  public void setIgnoreAspect(boolean ignore) {
+    ignoreAspect = ignore;
+  }
+  public void setPassword(String aPassword) {
+    password = aPassword;
+  }
+
+  public void enableAudio(boolean b) {
+    enableAudio = b;
+  }
+  public boolean isAudioEnabled() {
+    return enableAudio;
+  }
+
+  public void enableVideo(boolean b) {
+    enableVideo = b;
+  }
+  public boolean isVideoEnabled() {
+    return enableVideo;
+  }
+
+  /**
+   * Finds a Kate stream from the given language and category names.
+   * Returns a negative index if none is found.
+   */
+  private int findKateStream(String language, String category) {
+    int idx = -1;
+
+    boolean has_language = !language.equals("");
+    boolean has_category = !category.equals("");
+    if (has_language || has_category) {
+      for (int n=0; n<katedec.size(); ++n) {
+        Element e = (Element)katedec.elementAt(n);
+        if (e != null) {
+          String e_language = String.valueOf(e.getProperty("language"));
+          String e_category = String.valueOf(e.getProperty("category"));
+          if (language.equalsIgnoreCase(e_language)) {
+            if (!has_category || category.equals(e_category)) {
+              idx = n;
+            }
+          }
+        }
+      }
+    }
+    return idx;
+  }
+
+  /**
+   * Selects the Kate stream to enable, by index (if any), or by language/category.
+   * The first Kate stream has index 0, the second has index 1, etc.
+   * A negative index and empty language/category strings will enable none.
+   */
+  public void enableKateStream(int idx, String language, String category) {
+    if (idx < 0) idx = findKateStream(language, category);
+    if (idx == enableKate) return;
+    doEnableKateIndex(idx);
+  }
+
+  /**
+   * Enables the given Kate stream, by index.
+   * A negative index will enable none.
+   */
+  private void doEnableKateIndex(int idx)
+  {
+    if (kselector != null) {
+      Debug.info("Switching Kate streams from "+enableKate+" to "+idx);
+      kselector.setProperty("selected", new Integer(idx));
+    }
+    else {
+      Debug.warning("Switching Kate stream request, but no Kate selector exists");
+    }
+
+    enableKate = idx;
+  }
+
+  /**
+   * Returns the index of the currently enabled Kate stream (negative if none)
+   */
+  public int getEnabledKateIndex() {
+    return enableKate;
+  }
+
+  public void setComponent(Component c) {
+    component = c;
+  }
+  public Component getComponent() {
+    return component;
+  }
+
+  public void setDocumentBase(URL base) {
+    documentBase = base;
+  }
+  public URL getDocumentBase() {
+    return documentBase;
+  }
+
+  public void setBufferSize(int size) {
+    bufferSize = size;
+  }
+  public int getBufferSize() {
+    return bufferSize;
+  }
+
+  public void setBufferLow(int size) {
+    bufferLow = size;
+  }
+  public int getBufferLow() {
+    return bufferLow;
+  }
+
+  public void setBufferHigh(int size) {
+    bufferHigh = size;
+  }
+  public int getBufferHigh() {
+    return bufferHigh;
+  }
+
+  public void resize(Dimension d) {
+    if (videosink == null || d == null) {
+      return;
+    }
+    Rectangle bounds = new Rectangle(d);
+    if (application.getShowStatus() == Cortado.STATUS_SHOW) {
+      bounds.height -= application.getStatusHeight();
+    }
+    if (bounds.height < 0) {
+      bounds.height = 0;
+    }
+    videosink.setProperty("bounds", bounds);
+  }
+
+  public boolean buildOgg()
+  {
+    demux = ElementFactory.makeByName("oggdemux", "demux");
+    if (demux == null) {
+      noSuchElement ("oggdemux");
+      return false;
+    }
+
+    buffer = ElementFactory.makeByName("queue", "buffer");
+    if (buffer == null) {
+      demux = null;
+      noSuchElement ("queue");
+      return false;
+    }
+    buffer.setProperty("isBuffer", Boolean.TRUE);
+    if (bufferSize != -1)
+      buffer.setProperty("maxSize", new Integer (bufferSize * 1024));
+    if (bufferLow != -1)
+      buffer.setProperty("lowPercent", new Integer (bufferLow));
+    if (bufferHigh != -1)
+      buffer.setProperty("highPercent", new Integer (bufferHigh));
+
+    add(demux);
+    add(buffer);
+
+    httpsrc.getPad("src").link(buffer.getPad("sink"));
+    buffer.getPad("src").link(demux.getPad("sink"));
+    demux.addPadListener (this);
+
+    buffer.setState(PAUSE);
+    demux.setState(PAUSE);
+
+    return true;
+  }
+
+  public boolean buildMultipart()
+  {
+    demux = ElementFactory.makeByName("multipartdemux", "demux");
+    if (demux == null) {
+      noSuchElement ("multipartdemux");
+      return false;
+    }
+    add(demux);
+
+    httpsrc.getPad("src").link(demux.getPad("sink"));
+
+    demux.addPadListener (this);
+
+    return true;
+  }
+
+  public void capsChanged(Caps caps) {
+    String mime = caps.getMime();
+
+    if (mime.equals ("application/ogg")) {
+      buildOgg();
+    }
+    else if (mime.equals ("multipart/x-mixed-replace")) {
+      buildMultipart();
+    }
+    else {
+      postMessage (Message.newError (this, "unknown type: "+mime));
+    }
+  }
+  private void noSuchElement(String elemName)
+  {
+    postMessage (Message.newError (this, "no such element: "+elemName+" (check plugins.ini)"));
+  }
+
+  private boolean build()
+  {
+    Configure configure = new Configure();
+    String userAgent;
+    String extra;
+    String vendor = System.getProperty("java.vendor");
+
+    httpsrc = ElementFactory.makeByName("httpsrc", "httpsrc");
+    if (httpsrc == null) {
+      noSuchElement ("httpsrc");
+      return false;
+    }
+    
+    httpsrc.setProperty("url", url);
+    httpsrc.setProperty("userId", userId);
+    httpsrc.setProperty("password", password);
+    
+    userAgent = "Cortado/" + configure.buildVersion + " " +
+        vendor.substring(0, vendor.indexOf(" ")) + "/" +
+        System.getProperty("java.version");
+
+    extra = "(" + System.getProperty("os.name")  + " " +
+          System.getProperty("os.version") + ")";
+
+    try {
+      String agent = System.getProperty("http.agent");
+      if (agent != null) {
+        extra = agent;
+      }
+    }
+    catch (Exception e) {
+    }
+    userAgent += " " + extra;
+
+    Debug.log(Debug.INFO, "setting User-Agent " + userAgent);
+
+    httpsrc.setProperty("userAgent", userAgent);
+    httpsrc.setProperty("documentBase", documentBase);
+    add(httpsrc);
+
+    httpsrc.getPad("src").addCapsListener (this);
+
+    if (enableAudio) {
+		audiosink = newAudioSink();
+      if (audiosink == null) {
+	enableAudio = false; // to suppress creation of audio decoder pads
+	((Cortado)component).status.setHaveAudio(false);
+	component.repaint();
+	// Don't stop unless video is disabled too
+      } else {
+	asinkpad = audiosink.getPad("sink");
+	add(audiosink);
+      }
+    }
+    if (enableVideo) {
+      videosink = ElementFactory.makeByName("videosink", "videosink");
+      if (videosink == null) {
+        noSuchElement ("videosink");
+        return false;
+      }
+
+      videosink.setProperty("ignore-aspect", ignoreAspect ? "true" : "false");
+
+      videosink.setProperty ("component", component);
+      resize(component.getSize());
+
+      videosink.setProperty ("max-lateness", Long.toString(Clock.MSECOND * 20));
+      add(videosink);
+
+      ovsinkpad = videosink.getPad("sink");
+    }
+    if (audiosink == null && videosink == null) {
+      postMessage(Message.newError(this, "Both audio and video are disabled, can't play anything"));
+      return false;
+    }
+
+    return true;
+  }
+
+  protected Element newAudioSink() {
+    com.fluendo.plugin.AudioSink s;
+    try {
+      Class.forName("javax.sound.sampled.AudioSystem");
+      Class.forName("javax.sound.sampled.DataLine");
+      usingJavaX = true;
+      s = (com.fluendo.plugin.AudioSink)ElementFactory.makeByName("audiosinkj2", "audiosink");
+      Debug.log(Debug.INFO, "using high quality javax.sound backend");
+    }
+    catch (Throwable e) {
+      s = (com.fluendo.plugin.AudioSink)ElementFactory.makeByName("audiosinksa", "audiosink");
+      Debug.log(Debug.INFO, "using low quality sun.audio backend");
+    }
+    if (s == null) {
+      noSuchElement ("audiosink");
+      return null;
+    }
+    if (!s.test()) {
+      return null;
+    } else {
+      return s;
+    }
+  }
+
+  private boolean cleanup() {
+    int n;
+    Debug.log(Debug.INFO, "cleanup");
+    if (httpsrc != null) {
+      remove (httpsrc);
+      httpsrc = null;
+    }
+    if (audiosink != null) {
+      remove (audiosink);
+      audiosink = null;
+      asinkpad = null;
+    }
+    if (videosink != null) {
+      remove (videosink);
+      videosink = null;
+    }
+    if (overlay != null) {
+      remove (overlay);
+      overlay = null;
+      ovsinkpad = null;
+      oksinkpad = null;
+    }
+    if (buffer != null) {
+      remove (buffer);
+      buffer = null;
+    }
+    if (demux != null) {
+      demux.removePadListener (this);
+      remove (demux);
+      demux = null;
+    }
+    if (v_queue != null) {
+      remove (v_queue);
+      v_queue = null;
+    }
+    if (v_queue2 != null) {
+      remove (v_queue2);
+      v_queue2 = null;
+    }
+    if (a_queue != null) {
+      remove (a_queue);
+      a_queue = null;
+    }
+    if (videodec != null) {
+      remove(videodec);
+      videodec = null;
+    }
+    if (audiodec != null) {
+      remove(audiodec);
+      audiodec = null;
+    }
+
+    for (n=0; n<katedec.size(); ++n) {
+      if (k_queue.elementAt(n) != null) {
+        remove ((Element)k_queue.elementAt(n));
+      }
+      if (katedec.elementAt(n) != null) {
+        remove((Element)katedec.elementAt(n));
+      }
+    }
+    k_queue.removeAllElements();
+    katedec.removeAllElements();
+
+    if (kselector != null) {
+      remove(kselector);
+      kselector = null;
+    }
+
+    return true;
+  }
+
+  protected int changeState (int transition) {
+    int res;
+	  
+    switch (transition) {
+      case STOP_PAUSE:
+        if (!build())
+          return FAILURE;
+        break;
+      default:
+        break;
+    }
+
+    res = super.changeState (transition);
+
+    switch (transition) {
+      case PAUSE_STOP:
+        cleanup();
+        break;
+      default:
+        break;
+    }
+
+    return res;
+  }
+
+  protected boolean doSendEvent(com.fluendo.jst.Event event) {
+    boolean res;
+
+    if (event.getType() != com.fluendo.jst.Event.SEEK)
+      return false;
+
+    if (event.parseSeekFormat() != Format.PERCENT)
+      return false;
+
+    if (httpsrc == null)
+      return false;
+
+	res = httpsrc.getPad("src").sendEvent (event);
+    getState(null, null, -1);
+
+    return res;
+  }
+
+  protected long getPosition() {
+    Query q;
+    long result = 0;
+
+    q = Query.newPosition(Format.TIME);
+    if (super.query (q)) {
+      result = q.parsePositionValue ();
+    }
+    return result;
+  }
+}

Added: trunk/cronus/com/fluendo/player/DurationScanner.java
===================================================================
--- trunk/cronus/com/fluendo/player/DurationScanner.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/player/DurationScanner.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,262 @@
+/* Copyright (C) <2008> Maik Merten <maikmerten at googlemail.com>
+ * Copyright (C) <2004> Wim Taymans <wim at fluendo.com> (HTTPSrc.java parts)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+package com.fluendo.player;
+
+import com.fluendo.utils.Base64Converter;
+import com.fluendo.utils.Debug;
+import org.xiph.ogg.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.text.MessageFormat;
+import java.util.Hashtable;
+import java.util.Locale;
+
+/**
+ *
+ * @author maik
+ */
+public class DurationScanner {
+
+    final static int NOTDETECTED = -1;
+    final static int UNKNOWN = 0;
+    final static int VORBIS = 1;
+    final static int THEORA = 2;
+    private long contentLength = -1;
+    private long responseOffset;
+    private Hashtable streaminfo = new Hashtable();
+    private SyncState oy = new SyncState();
+    private Page og = new Page();
+    private Packet op = new Packet();
+
+    public DurationScanner() {
+        oy.init();
+    }
+
+    private InputStream openWithConnection(URL url, String userId, String password, long offset) throws IOException {
+        // lifted from HTTPSrc.java
+        InputStream dis = null;
+        String userAgent = "Cortado";
+
+        URLConnection uc = url.openConnection();
+
+        uc.setRequestProperty("Connection", "Keep-Alive");
+
+        String range;
+        if (offset != 0 && contentLength != -1) {
+            range = "bytes=" + offset + "-" + (contentLength - 1);
+        } else if (offset != 0) {
+            range = "bytes=" + offset + "-";
+        } else {
+            range = null;
+        }
+        if (range != null) {
+            Debug.info("doing range: " + range);
+            uc.setRequestProperty("Range", range);
+        }
+
+        uc.setRequestProperty("User-Agent", userAgent);
+        if (userId != null && password != null) {
+            String userPassword = userId + ":" + password;
+            String encoding = Base64Converter.encode(userPassword.getBytes());
+            uc.setRequestProperty("Authorization", "Basic " + encoding);
+        }
+        uc.setRequestProperty("Content-Type", "application/octet-stream");
+
+        /* This will send the request. */
+        dis = uc.getInputStream();
+
+        String responseRange = uc.getHeaderField("Content-Range");
+        if (responseRange == null) {
+            Debug.info("Response contained no Content-Range field, assuming offset=0");
+            responseOffset = 0;
+        } else {
+            try {
+                MessageFormat format = new MessageFormat("bytes {0,number}-{1,number}");
+                format.setLocale(Locale.US);
+                java.lang.Object parts[] = format.parse(responseRange);
+                responseOffset = ((Number) parts[0]).longValue();
+                if (responseOffset < 0) {
+                    responseOffset = 0;
+                }
+                Debug.debug("Stream successfully with offset " + responseOffset);
+            } catch (Exception e) {
+                Debug.info("Error parsing Content-Range header");
+                responseOffset = 0;
+            }
+        }
+
+        contentLength = uc.getHeaderFieldInt("Content-Length", -1) + responseOffset;
+
+        return dis;
+    }
+
+    private void determineType(Packet packet, StreamInfo info) {
+        // try theora
+        com.meviatronic.zeus.pollux.VideoReader ti = new com.meviatronic.zeus.pollux.VideoReader();
+
+		int ret = 0;
+		
+		try {
+			ti.readMediaInformation(packet);
+		} catch (Exception e) {
+			ret = -1;
+		}
+        if (ret == 0) {
+            info.decoder = ti;
+            info.type = THEORA;
+            info.decodedHeaders++;
+            return;
+		}
+		
+		// try vorbis
+        com.meviatronic.zeus.castor.AudioReader vi = new com.meviatronic.zeus.castor.AudioReader();
+
+		ret = 0;
+		
+		try {
+			vi.readMediaInformation(packet);
+		} catch (Exception e) {
+			ret = -1;
+		}
+        if (ret == 0) {
+            info.decoder = vi;
+            info.type = VORBIS;
+            info.decodedHeaders++;
+            return;
+        }
+        info.type = UNKNOWN;
+    }
+
+    public float getDurationForBuffer(byte[] buffer, int bufbytes) {
+        float time = -1;
+
+        int offset = oy.buffer(bufbytes);
+        java.lang.System.arraycopy(buffer, 0, oy.data, offset, bufbytes);
+        oy.wrote(bufbytes);
+
+        while (oy.pageout(og) == 1) {
+
+            Integer serialno = new Integer(og.serialno());
+            StreamInfo info = (StreamInfo) streaminfo.get(serialno);
+            if (info == null) {
+                info = new StreamInfo();
+                info.streamstate = new StreamState();
+                info.streamstate.init(og.serialno());
+                streaminfo.put(serialno, info);
+                Debug.info("DurationScanner: created StreamState for stream no. " + serialno);
+            }
+
+            info.streamstate.pagein(og);
+
+            while (info.streamstate.packetout(op) == 1) {
+
+                int type = info.type;
+                if (type == NOTDETECTED) {
+                    determineType(op, info);
+                    info.startgranule = og.granulepos();
+                }
+
+                switch (type) {
+                    case VORBIS:
+                         {
+                            com.meviatronic.zeus.castor.AudioReader i = (com.meviatronic.zeus.castor.AudioReader) info.decoder;
+                            float t = (float) (og.granulepos() - info.startgranule) / i.sampleRate;
+                            if (t > time) {
+                                time = t;
+                            }
+                        }
+                        break;
+                    case THEORA:
+                         {
+                            com.meviatronic.zeus.pollux.VideoReader i = (com.meviatronic.zeus.pollux.VideoReader) info.decoder;
+                        }
+                        break;
+                }
+            }
+        }
+
+        return time;
+
+    }
+
+    public float getDurationForURL(URL url, String user, String password) {
+        try {
+            int headbytes = 24 * 1024;
+            int tailbytes = 128 * 1024;
+
+            float time = 0;
+
+            byte[] buffer = new byte[1024];
+            InputStream is = openWithConnection(url, user, password, 0);
+
+            int read = 0;
+            long totalbytes = 0;
+            read = is.read(buffer);
+            // read beginning of the stream
+            while (totalbytes < headbytes && read > 0) {
+                totalbytes += read;
+                float t = getDurationForBuffer(buffer, read);
+                time = t > time ? t : time;
+                read = is.read(buffer);
+            }
+            is.close();
+            is = openWithConnection(url, user, password, contentLength - tailbytes);
+            if(responseOffset == 0) {
+                Debug.warning("DurationScanner: Couldn't complete duration scan due to failing range requests!");
+                return -1;
+            }
+
+            read = is.read(buffer);
+            // read tail until eos, also abort if way too many bytes have been read
+            while (read > 0 && totalbytes < (headbytes + tailbytes) * 2) {
+                totalbytes += read;
+                float t = getDurationForBuffer(buffer, read);
+                time = t > time ? t : time;
+                read = is.read(buffer);
+            }
+
+            return time;
+        } catch (IOException e) {
+            Debug.error(e.toString());
+            return -1;
+        }
+    }
+
+    private class StreamInfo {
+
+        public Object decoder;
+        public int decodedHeaders = 0;
+        public int type = NOTDETECTED;
+        public long startgranule;
+        public StreamState streamstate;
+        public boolean ready = false;
+    }
+
+    public static void main(String[] args) throws IOException {
+
+        URL url;
+        url = new URL(args[0]);
+
+
+        System.out.println(new DurationScanner().getDurationForURL(url, null, null));
+
+    }
+}

Added: trunk/cronus/com/fluendo/player/Status.java
===================================================================
--- trunk/cronus/com/fluendo/player/Status.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/player/Status.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,603 @@
+/* Cortado - a video player java applet
+ * Copyright (C) 2004 Fluendo S.L.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.player;
+
+import java.awt.*;
+import java.awt.image.*;
+import java.awt.event.*;
+import java.util.*;
+
+public class Status extends Component implements MouseListener,
+        MouseMotionListener {
+    private static final long serialVersionUID = 1L;
+
+    private int bufferPercent;
+    private boolean buffering;
+
+    private String message;
+    private String error;
+
+    private Rectangle r;
+    private Component component;
+
+    private Font font = new Font("SansSerif", Font.PLAIN, 10);
+
+    private boolean haveAudio;
+    private boolean havePercent;
+    private boolean seekable;
+    private boolean live;
+    private boolean showSpeaker;
+    private boolean clearedScreen;
+
+    private static final int NONE = 0;
+    private static final int BUTTON1 = 1;
+    private static final int BUTTON2 = 2;
+    private static final int SEEKER = 3;
+    private static final int SEEKBAR = 4;
+    private int clicked = NONE;
+
+    private Color button1Color;
+    private Color button2Color;
+    private Color seekColor;
+
+    private static final int SPEAKER_WIDTH = 12;
+    private static final int SPEAKER_HEIGHT = 10;
+    private static final int TIME_WIDTH = 38;
+    private static final int SEEK_TIME_GAP = 10;
+    private static final int THUMB_WIDTH = 9;
+
+    public static final int STATE_STOPPED = 0;
+    public static final int STATE_PAUSED = 1;
+    public static final int STATE_PLAYING = 2;
+
+    private int state = STATE_STOPPED;
+
+    private double position = 0;
+    private long time;
+    private double duration;
+    private long byteDuration;
+    private long bytePosition;
+
+    private String speaker = "\0\0\0\0\0\357\0\0\357U\27"
+            + "\36\0\0\0\0\357\357\0\0" + "\0\357U\30\0\0\0\357\0\357"
+            + "\0\357\0\0\357\23\357" + "\357\357\0\34\357\0Z\357\0"
+            + "\357\\\357\0)+F\357\0\0\357" + "\0\357r\357Ibz\221\357"
+            + "\0\0\357\0\357r\357\357\357" + "\276\323\357\0Z\357\0\357"
+            + "\\\0\0\0\357\357\357\0" + "\357\0\0\357\0\0\0\0\0\357"
+            + "\357\0\0\0\357\\\0\0\0" + "\0\0\0\357\0\0\357\\\0\0";
+
+    private Image speakerImg;
+    private int speakerWidth; // width of the speaker icon or zero if hidden
+
+
+    private Vector listeners = new Vector();
+
+    public Status(Component comp) {
+        int[] pixels = new int[SPEAKER_WIDTH * SPEAKER_HEIGHT];
+        component = comp;
+
+        for (int i = 0; i < SPEAKER_WIDTH * SPEAKER_HEIGHT; i++) {
+            pixels[i] = 0xff000000 | (speaker.charAt(i) << 16)
+                    | (speaker.charAt(i) << 8) | (speaker.charAt(i));
+        }
+        speakerImg = comp.getToolkit().createImage(
+                new MemoryImageSource(SPEAKER_WIDTH, SPEAKER_HEIGHT, pixels, 0, SPEAKER_WIDTH));
+        button1Color = Color.black;
+        button2Color = Color.black;
+        seekColor = Color.black;
+    }
+
+    public void addStatusListener(StatusListener l) {
+        listeners.addElement(l);
+    }
+
+    public void removeStatusListener(StatusListener l) {
+        listeners.removeElement(l);
+    }
+
+    public void notifyNewState(int newState) {
+        for (Enumeration e = listeners.elements(); e.hasMoreElements();) {
+            ((StatusListener) e.nextElement()).newState(newState);
+        }
+    }
+
+    public void notifySeek(double position) {
+        for (Enumeration e = listeners.elements(); e.hasMoreElements();) {
+            ((StatusListener) e.nextElement()).newSeek(position);
+        }
+    }
+
+    public void update(Graphics g) {
+        paint(g);
+    }
+
+    private void paintBox(Graphics g) {
+        g.setColor(Color.darkGray);
+        g.drawRect(0, 0, r.width - 1, r.height - 1);
+        g.setColor(Color.black);
+        g.fillRect(1, 1, r.width - 2, r.height - 2);
+    }
+
+    private void paintPercent(Graphics g) {
+        if (havePercent) {
+            g.setColor(Color.white);
+            g.drawString("" + bufferPercent + "%", r.width - 26 - speakerWidth, r.height - 2);
+        }
+    }
+
+    private void paintButton1(Graphics g) {
+	int x,y,w,h;
+
+	x = 1;
+	y = 1;
+	w = r.height-2;
+	h = r.height-2;
+        g.setColor(Color.darkGray);
+        g.drawRect(x, y, w, h);
+        g.setColor(button1Color);
+        g.fillRect(x+1, y+1, w-1, h-1);
+
+        if (state == STATE_PLAYING) {
+            g.setColor(Color.white);
+            if (live) {
+	      /* STOP */
+              g.fillRect((int)(w * .4), (int)(w * .4), (int)(w * .5), (int)(w * .5));
+	    }
+	    else {
+	      /* PAUSE */
+              g.fillRect((int)(w * .4), (int)(h * .4), (int)(w * .2), (int)(h * .5));
+              g.fillRect((int)(w * .7), (int)(h * .4), (int)(w * .2), (int)(h * .5));
+	    }
+        } else {
+            int triangleX[] = { (int)(w*.4), (int)(w*.4), (int)(w*.9) };
+            int triangleY[] = { (int)(w*.3), (int)(w*.9), (int)(w*.6) };
+            g.setColor(Color.white);
+            g.fillPolygon(triangleX, triangleY, 3);
+        }
+    }
+
+    private void paintButton2(Graphics g) {
+	int x,y,w,h;
+
+	x = r.height + 1;
+	y = 1;
+	w = r.height - 2;
+	h = r.height - 2;
+
+        g.setColor(Color.darkGray);
+        g.drawRect(x, y, w, h);
+        g.setColor(button2Color);
+        g.fillRect(x+1, y+1, w-1, h-1);
+        g.setColor(Color.white);
+        g.fillRect(r.height + (int)(w * .4), (int)(w * .4), (int)(w * .5), (int)(w * .5));
+    }
+
+    private void paintMessage(Graphics g, int pos) {
+        if (message != null) {
+            g.setColor(Color.white);
+            g.drawString(message, pos, r.height - 2);
+        }
+    }
+
+    private void paintBuffering(Graphics g, int pos) {
+        g.setColor(Color.white);
+        g.drawString("Buffering", pos, r.height - 2);
+    }
+
+    /*
+     * Get the inclusive bounding rectangle of the seek bar
+     */
+    private Rectangle getSeekBarRect() {
+      return new Rectangle(r.height*2 + 1, 2,
+	  r.width - SEEK_TIME_GAP - TIME_WIDTH - speakerWidth - (r.height * 2),
+	  r.height - 4);
+    }
+
+    /*
+     * Get the inclusive bounding rectangle of the seek bar thumb
+     */
+    private Rectangle getThumbRect() {
+      Rectangle seekRect = getSeekBarRect();
+      int availableWidth = seekRect.width - THUMB_WIDTH;
+      int pos = (int)(availableWidth * position);
+      return new Rectangle(pos + seekRect.x, 1, THUMB_WIDTH, r.height - 2);
+    }
+
+    private void paintSeekBar(Graphics g) {
+        Rectangle sr = getSeekBarRect();
+        Rectangle tr = getThumbRect();
+
+        // Bounding rectangle
+        g.setColor(Color.darkGray);
+        g.drawRect(sr.x, sr.y, sr.width, sr.height);
+
+        // Progress bar
+        g.setColor(Color.gray);
+        g.fillRect(sr.x + 2, sr.y + 3, tr.x - (sr.x + 2), sr.height - 6);
+        
+        // Thumb
+        g.setColor(Color.white);
+        g.drawLine(tr.x + 1,        tr.y,             tr.x + tr.width - 1, tr.y);                 // Top
+        g.drawLine(tr.x + 1,        tr.y + tr.height, tr.x + tr.width - 1, tr.y + tr.height);     // Bottom
+        g.drawLine(tr.x,            tr.y + 1,         tr.x,                tr.y + tr.height - 1); // Left
+        g.drawLine(tr.x + tr.width, tr.y + 1,         tr.x + tr.width,     tr.y + tr.height - 1); // Right
+
+        // Thumb interior
+        g.setColor(seekColor);
+        g.fillRect(tr.x + 1, tr.y + 1, tr.width - 1, tr.height - 1);
+    }
+
+    private void paintTime(Graphics g) {
+        long hour, min, sec;
+        int end;
+
+        if (time < 0)
+            return;
+
+        sec = time % 60;
+        min = time / 60;
+        hour = min / 60;
+        min %= 60;
+
+        r = getBounds();
+
+        end = r.width - speakerWidth - TIME_WIDTH;
+
+        g.setColor(Color.white);
+        g.drawString("" + hour + ":" + (min < 10 ? "0" + min : "" + min) + ":"
+                + (sec < 10 ? "0" + sec : "" + sec), end, r.height - 2);
+    }
+
+    private void paintSpeaker(Graphics g) {
+        if (haveAudio) {
+            g.drawImage(speakerImg, r.width - SPEAKER_WIDTH, r.height - SPEAKER_HEIGHT - 1, null);
+        }
+    }
+
+    public void paint(Graphics g) {
+
+        if (!isVisible() && clearedScreen)
+            return;
+
+        r = getBounds();
+        
+        if(!isVisible() && !clearedScreen) {
+            g.clearRect(r.x, r.y, r.width, r.height);
+            clearedScreen = true;
+            return;
+        }
+        clearedScreen = false;
+
+        int pos = 0;
+
+        Image img = component.createImage(r.width, r.height);
+	if (img == null)
+          return;
+        Graphics g2 = img.getGraphics();
+	if (g2 == null)
+          return;
+        g2.setFont(font);
+
+        paintBox(g2);
+	if (!buffering) {
+            paintButton1(g2);
+	}
+	if (!live) {
+          paintButton2(g2);
+	  pos = r.height*2;
+	}
+	else {
+	  pos = r.height;
+	}
+        if (buffering) {
+            paintPercent(g2);
+            paintBuffering(g2, pos + 3);
+	}
+        else if (state == STATE_STOPPED || !seekable) {
+            paintMessage(g2, pos + 3);
+            paintTime(g2);
+	}
+	else if (seekable) {
+            paintSeekBar(g2);
+            paintTime(g2);
+	}
+        if (showSpeaker) {
+            paintSpeaker(g2);
+        }
+
+        g.drawImage(img, r.x, r.y, null);
+        img.flush();
+    }
+
+    public void setBufferPercent(boolean buffering, int bp) {
+	boolean changed;
+
+	changed = this.buffering != buffering;
+	changed |= this.bufferPercent != bp;
+
+	if (changed) {
+          this.buffering = buffering;
+          this.bufferPercent = bp;
+          component.repaint();
+	}
+    }
+
+    public void setTime(double seconds) {
+        if (clicked == NONE) {
+            double newPosition;
+	    
+            if (seconds < duration || seekable)
+                time = (long) seconds;
+            else
+                time = (long) duration;
+
+            if(duration > -1) {
+                newPosition = ((double) time) / duration;
+                if (newPosition != position) {
+                    position = newPosition;
+                    component.repaint();
+                }
+            } else {
+                newPosition = ((double)bytePosition) / (double)byteDuration;
+                position = newPosition;
+                component.repaint();
+            }
+        }
+    }
+
+    public void setDuration(double seconds) {
+        duration = seconds;
+        component.repaint();
+    }
+    
+    public void setByteDuration(long bytes) {
+        this.byteDuration = bytes;
+        if(duration == -1) {
+            position = ((double)bytePosition) / (double)byteDuration;
+            component.repaint();
+        }
+    }
+    
+    public void setBytePosition(long bytes) {
+        this.bytePosition = bytes;
+        if(duration == -1) {
+            position = ((double)bytePosition) / (double)byteDuration;
+            component.repaint();
+        }
+    }
+
+    public void setMessage(String m) {
+        message = m;
+        component.repaint();
+    }
+
+    public void setHaveAudio(boolean a) {
+        haveAudio = a;
+        component.repaint();
+    }
+
+    public void setHavePercent(boolean p) {
+        havePercent = p;
+        component.repaint();
+    }
+
+    public void setSeekable(boolean s) {
+        seekable = s;
+        component.repaint();
+    }
+
+    public void setLive(boolean l) {
+        live = l;
+        component.repaint();
+    }
+
+    public void setShowSpeaker(boolean s) {
+        showSpeaker = s;
+        speakerWidth = s ? SPEAKER_WIDTH : 0;
+        component.repaint();
+    }
+
+    public void setState(int aState) {
+	if (state != aState) {
+          state = aState;
+          component.repaint();
+	}
+    }
+
+    private boolean intersectButton1(MouseEvent e) {
+        if (r == null)
+            return false;
+
+        return (e.getX() >= 0 && e.getX() <= r.height-2 && e.getY() > 0 && e.getY() <= r.height-2);
+    }
+
+    private boolean intersectButton2(MouseEvent e) {
+        if (r == null)
+            return false;
+
+        return (e.getX() >= r.height && e.getX() <= r.height + r.height-2 && e.getY() > 0 && e.getY() <= r.height-2);
+    }
+
+    private boolean intersectSeeker(MouseEvent e) {
+        r = getBounds();
+	Rectangle tr = getThumbRect();
+	return tr.contains(e.getPoint());
+    }
+
+    private boolean intersectSeekbar(MouseEvent e) {
+        r = getBounds();
+	Rectangle sr = getSeekBarRect();
+	return sr.contains(e.getPoint());
+    }
+
+    private int findComponent(MouseEvent e) {
+        if (!buffering && intersectButton1(e))
+            return BUTTON1;
+        else if (intersectButton2(e))
+            return BUTTON2;
+        else if (seekable && intersectSeeker(e))
+            return SEEKER;
+        else if (seekable && intersectSeekbar(e))
+            return SEEKBAR;
+        else
+            return NONE;
+    }
+
+    public void cancelMouseOperation() {
+      button1Color = Color.black;
+      button2Color = Color.black;
+      seekColor = Color.black;
+      clicked = NONE;
+    }
+
+    public void mouseClicked(MouseEvent e) {
+    }
+
+    public void mouseEntered(MouseEvent e) {
+    }
+
+    public void mouseExited(MouseEvent e) {
+    }
+
+    public void mousePressed(MouseEvent e) {
+        e.translatePoint(-1, -1);
+        clicked = findComponent(e);
+        if (clicked == SEEKBAR && state != STATE_STOPPED) {
+          clicked = SEEKER;
+          seekColor = Color.gray;
+	  mouseDragged (e);
+        }
+    }
+
+    public void mouseReleased(MouseEvent e) {
+        int comp;
+
+        e.translatePoint(-1, -1);
+
+        comp = findComponent(e);
+        if (clicked != comp) {
+            if (clicked == SEEKER)
+                comp = clicked;
+            else
+                return;
+        }
+
+        switch (comp) {
+        case BUTTON1:
+            if (state == STATE_PLAYING) {
+		if (live)
+                  state = STATE_STOPPED;
+		else
+                  state = STATE_PAUSED;
+                notifyNewState(state);
+            } else {
+                state = STATE_PLAYING;
+                notifyNewState(state);
+            }
+            break;
+        case BUTTON2:
+            state = STATE_STOPPED;
+            notifyNewState(state);
+            break;
+        case SEEKER:
+            if (state != STATE_STOPPED)
+              notifySeek(position);
+            break;
+        case SEEKBAR:
+            break;
+        case NONE:
+            break;
+        }
+        clicked = NONE;
+        component.repaint();
+    }
+
+    public void mouseDragged(MouseEvent e) {
+        if (seekable) {
+            e.translatePoint(-1, -1);
+            if (clicked == SEEKER) {
+	        Rectangle sr = getSeekBarRect();
+		int availableWidth = sr.width - THUMB_WIDTH;
+		// If the midpoint of the thumb is at the cursor, where would the left of the thumb be?
+		// (relative to sr.x)
+		int thumbLeft = e.getX() - sr.x - THUMB_WIDTH / 2;
+		double newPosition = thumbLeft / (double)availableWidth;
+
+                if (newPosition < 0.0)
+                    newPosition = 0.0;
+                else if (newPosition > 1.0)
+                    newPosition = 1.0;
+
+		if (newPosition != position) {
+	          position = newPosition;
+                  time = (long) (duration * position);
+                  component.repaint();
+		}
+            }
+        }
+    }
+
+    public void mouseMoved(MouseEvent e) {
+        boolean needRepaint = false;
+
+        e.translatePoint(-1, -1);
+
+	if (!buffering) {
+            if (intersectButton1(e)) {
+	        if (button1Color != Color.gray) {
+                    button1Color = Color.gray;
+	            needRepaint = true;
+                }
+            } else {
+	        if (button1Color != Color.black) {
+                    button1Color = Color.black;
+	            needRepaint = true;
+	        }
+	    }
+	}
+        if (intersectButton2(e)) {
+            if (button2Color != Color.gray) {
+                button2Color = Color.gray;
+                needRepaint = true;
+            }
+        } else {
+	    if (button2Color != Color.black) {
+                button2Color = Color.black;
+	        needRepaint = true;
+	    }
+	}
+
+        if (seekable) {
+            if (intersectSeeker(e)) {
+                if (seekColor != Color.gray) {
+                    seekColor = Color.gray;
+                    needRepaint = true;
+		}
+            } else {
+                if (seekColor != Color.black) {
+                    seekColor = Color.black;
+                    needRepaint = true;
+                }
+            }
+        }
+	if (needRepaint)
+          component.repaint();
+    }
+}
+

Added: trunk/cronus/com/fluendo/player/StatusListener.java
===================================================================
--- trunk/cronus/com/fluendo/player/StatusListener.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/player/StatusListener.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,25 @@
+/* Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.player;
+
+public interface StatusListener
+{
+  public void newState (int newState);
+  public void newSeek (double position);
+}

Added: trunk/cronus/com/fluendo/plugin/AudioSink.java
===================================================================
--- trunk/cronus/com/fluendo/plugin/AudioSink.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/plugin/AudioSink.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,549 @@
+/* Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.plugin;
+
+import com.fluendo.jst.*;
+import com.fluendo.utils.*;
+
+public abstract class AudioSink extends Sink implements ClockProvider
+{
+  protected RingBuffer ringBuffer = null;
+
+  private class AudioClock extends SystemClock {
+    private long lastTime = -1;
+    private long diff = -1;
+    private boolean started = false;
+
+    public void setStarted(boolean s) {
+      started = s;
+      if (started) {
+        diff = -1;
+        lastTime = -1;
+      }
+    }
+
+    protected long getInternalTime() {
+      long samples;
+      long result;
+      long timePos;
+      long now;
+      
+      synchronized (ringBuffer) {
+        if (ringBuffer == null || ringBuffer.rate == 0)
+          return 0;
+      
+        samples = ringBuffer.samplesPlayed();
+        timePos = samples * Clock.SECOND / ringBuffer.rate;
+
+        if (started) {
+          /* interpolate as the position can jump a lot */
+          now = System.currentTimeMillis() * Clock.MSECOND;
+          if (diff == -1) {
+            diff = now;
+          }
+
+          if (timePos != lastTime) {
+            lastTime = timePos;
+            diff = now - timePos;
+          }
+          result = now - diff;
+          //System.out.println("time: "+result+", now: "+now+", diff: "+diff+", timePos: "+timePos);
+        }
+        else {
+          result = timePos;
+          //System.out.println("time: "+result);
+        }
+      }
+      //System.out.println("time: "+result+" samples: "+samples+" sampletime: "+timePos);
+
+      return result;
+    }
+  };
+  private AudioClock audioClock = new AudioClock();
+
+  public Clock provideClock() {
+    return audioClock;
+  }
+
+  protected class RingBuffer implements Runnable {
+    protected byte[] buffer;
+    private int state;
+    private Thread thread;
+    private long nextSample;
+    private boolean flushing;
+    private boolean autoStart;
+    private boolean opened;
+
+    private static final int STOP = 0;
+    private static final int PAUSE = 1;
+    private static final int PLAY = 2;
+
+    public int bps, sps;
+    public byte[] emptySeg;
+    public long playSeg;
+    public int segTotal;
+    public int segSize;
+    public int rate, channels;
+
+    public void run() {
+      boolean running = true;
+
+      while (running) {
+	synchronized (this) {
+	  if (state != PLAY) {
+	    while (state == PAUSE) {
+	      try {
+                notifyAll();
+	        wait();
+	      }
+	      catch (InterruptedException ie) {}
+	    }
+	    if (state == STOP) {
+	      running = false;
+	      break;
+	    }
+	  }
+	}
+        
+        int segNum = (int) (playSeg % segTotal);
+        int index = segNum * segSize;
+	int ret, toWrite;
+
+        toWrite = segSize;
+        while (toWrite > 0) {
+	  ret = write (buffer, index, segSize);
+	  if (ret == -1)
+	    break;
+
+	  toWrite -= ret;
+	}
+
+        clear (segNum);
+
+	synchronized (this) {
+	  //System.out.println("writen seg "+playSeg);
+	  playSeg++;
+	  notifyAll();
+	}
+      }
+    }
+
+    public synchronized void setFlushing(boolean flushing) {
+      this.flushing = flushing;
+      clearAll();
+      if (flushing) {
+        pause();
+      }
+    }
+
+    protected void startWriteThread ()
+    {
+      thread = new Thread(this, "cortado-audiosink-ringbuffer");
+      thread.start();
+      try {
+        wait();
+      }
+      catch (InterruptedException ie) {}
+    }
+
+    public synchronized boolean acquire(Caps caps) {
+      boolean res;
+
+      if (thread != null)
+        return false;
+ 
+      if (opened)
+        return false;
+
+      String mime = caps.getMime();
+      if (!mime.equals ("audio/raw"))
+        return false;
+
+      rate = caps.getFieldInt("rate", 44100);
+      channels = caps.getFieldInt("channels", 1);
+      bps = 2 * channels;
+
+      if ((res = open (this)) == false)
+        return res;
+      
+      opened = true;
+
+      Debug.log(Debug.INFO, "audio: segSize: "+ segSize);
+      Debug.log(Debug.INFO, "audio: segTotal: "+ segTotal);
+
+      segTotal++;
+
+      buffer = new byte[segSize * segTotal];
+      sps = segSize / bps;
+
+      state = PAUSE;
+      nextSample = 0;
+      playSeg = 0;
+
+      startWriteThread();
+
+      return res;
+    }
+    public synchronized boolean isAcquired() {
+      return opened;
+    }
+    public boolean release() {
+      stop();
+
+      synchronized (this) {
+        if (opened) {
+          if (close(this) == false)
+	    return false;
+        }
+        opened = false;
+      }
+
+      return true;
+    }
+
+    private synchronized boolean waitSegment() {
+      if (flushing)
+        return false;
+
+      if (state != PLAY && autoStart) {
+        play();
+      }
+
+      try {
+	if (state != PLAY) {
+	  //System.out.println("waitSegment: not playing");
+	  return false;
+	}
+	    
+        wait();
+        if (flushing)
+          return false;
+
+	if (state != PLAY)
+	  return false;
+      }
+      catch (InterruptedException ie) {}
+
+      return true;
+    }
+
+    public int commit (byte[] data, long sample, int offset, int len) {
+      int idx;
+
+      if (sample == -1) {
+        sample = nextSample;
+      }
+      if (sample < 0) {
+        return len;
+      }
+      if (nextSample != -1) {
+        if (Math.abs(sample - nextSample) < (rate / 10))
+          sample = nextSample;
+	else
+          System.out.println("discont: found "+sample+" expected "+nextSample);
+      }
+
+      idx = 0;
+
+      nextSample = sample + (len / bps);
+      while (len > 0) {
+        long writeSeg;
+	int writeOff;
+	int writeLen = 0;
+	long diff = -1;
+
+	writeSeg = sample / sps;
+	writeOff = (int) ((sample % sps) * bps);
+
+	while (true) {
+	  /* get the currently playing segment */
+	  synchronized (this) {
+	    /* see how far away it is from the write segment */
+	    diff = writeSeg - playSeg;
+	  }
+
+          /* play segment too far ahead, we need to drop */
+          if (diff < 0) {
+            /* we need to drop one segment at a time, pretend we wrote a
+             * segment. */
+            writeLen = Math.min (segSize, len);
+	    //System.out.println("dropped "+diff);
+            break;
+          }
+	  else {
+            /* write segment is within writable range, we can break the loop and
+             * start writing the data. */
+            if (diff < segTotal)
+              break;
+
+            /* else we need to wait for the segment to become writable. */
+	    //System.out.println("wait "+diff);
+            if (!waitSegment ()) {
+	      //System.out.println("flushing");
+              return -1;
+	    }
+	  }
+        }
+	if (diff >= 0) {
+	  int writeSegRel;
+
+          /* we can write now */
+          writeSegRel = (int) (writeSeg % segTotal);
+	  writeLen = Math.min (segSize - writeOff, len);
+
+          System.arraycopy (data, idx, buffer, writeSegRel * segSize + writeOff, writeLen);
+	}
+
+        len -= writeLen;
+        idx += writeLen;
+        sample += writeLen / bps;
+      }
+
+      return len;
+    }
+
+    public long samplesPlayed () {
+      long delay, samples;
+      long seg;
+
+      /* get the number of samples not yet played */
+      delay = delay ();
+      
+      seg = Math.max (0, playSeg - 1);
+
+      samples = (seg * sps);
+
+      //System.out.println("samples: "+samples+" delay: "+delay);
+
+      if (samples >= delay)
+        samples -= delay;
+      else
+        samples = 0;
+
+      return samples;
+    }
+    public synchronized void clear (long segNum)
+    {
+      int index = ((int)(segNum % segTotal)) * segSize;
+
+      System.arraycopy (emptySeg, 0, buffer, index, segSize);
+    }
+    public synchronized void clearAll ()
+    {
+      for (int i = 0; i < segTotal; i++) {
+        clear (i);
+      }
+    }
+    public synchronized void setSample (long sample) {
+      //System.out.println("setSample: "+sample);
+
+      if (sample == -1)
+        sample = 0;
+
+      playSeg = sample / sps;
+      nextSample = sample;
+
+      clearAll();
+    }
+
+    public synchronized void setAutoStart (boolean start) {
+      autoStart = start;
+    }
+    public boolean play () {
+      synchronized (this) {
+        if (flushing)
+          return false;
+
+        state = PLAY;
+        audioClock.setStarted(true);
+        notifyAll();
+      }
+      Debug.log(Debug.DEBUG, this+" playing");
+      return true;
+    }
+    public boolean pause () {
+      synchronized (this) {
+        Debug.log(Debug.DEBUG, this+" pausing");
+        state = PAUSE;
+        audioClock.setStarted(false);
+        notifyAll();
+        if (thread != null) {
+          try {
+	    Debug.log(Debug.DEBUG, this+" waiting for pause");
+            wait();
+          }
+          catch (InterruptedException ie) {}
+        }
+      }
+      Debug.log(Debug.DEBUG, this+" paused");
+      return true;
+    }
+    public boolean stop () {
+      synchronized (this) {
+        Debug.log(Debug.DEBUG, this+" stopping");
+        state = STOP;
+        audioClock.setStarted(false);
+        notifyAll();
+      }
+      if (thread != null) {
+        try {
+          Debug.log(Debug.DEBUG, this+" joining thread");
+          thread.join();
+	  thread = null;
+        }
+        catch (InterruptedException ie) {}
+      }
+      Debug.log(Debug.DEBUG, this+" stopped");
+      return true;
+    }
+    public synchronized int getState() {
+      return state;
+    }
+  }
+
+  // Test whether the audio sink is likely to work.
+  // Called before the ring buffer is acquired
+  public boolean test() {
+    return true;
+  }
+
+  protected WaitStatus doSync (long time)
+  {
+    return WaitStatus.newOK();
+  }
+  protected boolean doEvent (Event event)
+  {
+    switch (event.getType()) {
+      case Event.FLUSH_START:
+	ringBuffer.setFlushing (true);
+        break;
+      case Event.FLUSH_STOP:
+	ringBuffer.setFlushing (false);
+        break;
+      case Event.NEWSEGMENT:
+        break;
+      case Event.EOS:
+	// wait for completion, perform blocking drain of buffers
+	drain();
+        break;
+    }
+    return true;
+  }
+  protected int render (Buffer buf)
+  {
+    long sample;
+    long time;
+
+    if (buf.isFlagSet (com.fluendo.jst.Buffer.FLAG_DISCONT))
+      ringBuffer.nextSample = -1;
+
+    time = buf.timestamp - segStart;
+    if (time < 0)
+      return Pad.OK;
+    time += baseTime;
+
+    sample = time * ringBuffer.rate / Clock.SECOND;
+
+    //System.out.println("render sample: "+sample+" time: "+buf.timestamp);
+
+    ringBuffer.commit (buf.data, sample, buf.offset, buf.length);
+
+    return Pad.OK;
+  }
+
+  protected boolean setCapsFunc (Caps caps)
+  {
+    boolean res;
+
+    ringBuffer.release();
+    res = ringBuffer.acquire(caps);
+    return res;
+  }
+
+  protected int changeState (int transition) {
+    int result;
+
+    switch (transition) {
+      case STOP_PAUSE:
+        ringBuffer = createRingBuffer();
+	ringBuffer.setFlushing(false);
+        break;
+      case PAUSE_PLAY:
+        //long sample = baseTime * ringBuffer.rate / Clock.SECOND;
+	//ringBuffer.setSample (sample);
+	ringBuffer.setAutoStart (true);
+        break;
+      case PLAY_PAUSE:
+        reset();
+	ringBuffer.setAutoStart (false);
+        ringBuffer.pause();
+        break;
+      case PAUSE_STOP:
+	ringBuffer.setFlushing(true);
+        break;
+    }
+    result = super.changeState(transition);
+
+    switch (transition) {
+      case PAUSE_STOP:
+        ringBuffer.release();
+        break;
+    }
+
+    return result;
+  }
+
+  /*
+   * Block until audio playback is finished
+   */
+  protected void drain() {
+    if (ringBuffer.rate <= 0) {
+      return;
+    }
+
+    /* need to start playback before we can drain, but only when
+     * we have successfully negotiated a format and thus acquired the
+     * ringbuffer. */
+    if (!ringBuffer.isAcquired()) {
+      // FIXME make it work like it does in GstBaseAudioSink
+      // ringBuffer.acquire(...);
+      return;
+    }
+
+    if (ringBuffer.getState() != PLAY) {
+      ringBuffer.play();
+    }
+
+    if (ringBuffer.nextSample != -1) {
+      long time = ringBuffer.nextSample * Clock.SECOND / ringBuffer.rate;
+      Clock.ClockID id = audioClock.newSingleShotID(time);
+      Debug.log(Debug.DEBUG, this+" waiting until t=" + ((double)time / Clock.SECOND) + "s for playback to finish");
+      id.waitID();
+      ringBuffer.nextSample = -1;
+    }
+  }
+
+  protected abstract RingBuffer createRingBuffer();
+  protected abstract boolean open (RingBuffer ring);
+  protected abstract boolean close (RingBuffer ring);
+  protected abstract int write (byte[] data, int offset, int length);
+  protected abstract long delay ();
+  protected abstract void reset ();
+}
+

Added: trunk/cronus/com/fluendo/plugin/AudioSinkJ2.java
===================================================================
--- trunk/cronus/com/fluendo/plugin/AudioSinkJ2.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/plugin/AudioSinkJ2.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,219 @@
+/* Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.plugin;
+
+import com.fluendo.jst.*;
+import com.fluendo.utils.*;
+import javax.sound.sampled.*;
+
+public class AudioSinkJ2 extends AudioSink
+{
+  public static final int SEGSIZE = 2048;
+
+  private SourceDataLine line = null;
+  private int channels;
+  private long samplesWritten;
+
+  protected RingBuffer createRingBuffer() {
+    return new RingBuffer();
+  }
+
+  protected boolean open (RingBuffer ring) {
+    channels = ring.channels;
+    line = openLine(ring.channels, ring.rate);
+    if (line == null) {
+      postMessage (Message.newError (this, "Could not open audio device."));
+      return false;
+    }
+
+    Debug.log(Debug.INFO, "line info: available: "+ line.available());
+    Debug.log(Debug.INFO, "line info: buffer: "+ line.getBufferSize());
+    Debug.log(Debug.INFO, "line info: framePosition: "+ line.getFramePosition());
+
+    ring.segSize = SEGSIZE*channels*2;
+    ring.segTotal = line.getBufferSize() / ring.segSize;
+    while (ring.segTotal < 4) {
+      ring.segSize >>= 1;
+      ring.segTotal = line.getBufferSize() / ring.segSize;
+    }
+
+    ring.emptySeg = new byte[ring.segSize];
+    samplesWritten = 0;
+
+    line.start();
+
+    return true;
+  }
+
+  protected SourceDataLine openLine(int channels, int rate) {
+    AudioFormat format = new AudioFormat(rate, 16, channels, true, true);
+    DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
+    SourceDataLine line = null;
+
+    try {
+      Mixer.Info[] mixers = AudioSystem.getMixerInfo();
+
+      /* On linux, the default implementation gives terribly inaccurate results
+       * from line.available(), so we can't keep sync. Modern JVMs have an ALSA
+       * implementation that doesn't suck, so use that if available. */
+      for(int i=0; i < mixers.length; i++) {
+        Debug.log(Debug.INFO, "mixer description: " +
+                mixers[i].getDescription() + ", vendor: " +
+                mixers[i].getVendor());
+        /* Apparently either description or vendor might contain 'ALSA' - on
+         * my system, it's vendor */
+        String desc = mixers[i].getDescription();
+        String vendor = mixers[i].getVendor();
+        if(desc.indexOf("ALSA") >= 0 ||
+           vendor.indexOf("ALSA") >= 0)
+        {
+          /* Unfortunately, the alsa devices include useless ones that we have
+           * no sane way of filtering out! Hence this insanity. */
+          if (desc.indexOf("IEC958") >= 0)
+            continue;
+
+          try {
+            Line.Info[] lines = AudioSystem.getMixer(mixers[i]).
+                getSourceLineInfo(info);
+
+            for (int j=0; j < lines.length; j++) {
+              Debug.log(Debug.INFO, "Mixer supports line: " +
+                  lines[j].toString());
+              AudioFormat[] formats = ((DataLine.Info)lines[j]).getFormats();
+              for(int k=0; k < formats.length; k++)
+                Debug.log(Debug.INFO, "Format: " + formats[k].toString());
+            }
+            Debug.log(Debug.INFO, "Attempting to get a line from ALSA mixer");
+            line = (SourceDataLine) AudioSystem.getMixer(
+                mixers[i]).getLine(info);
+            /* Got one. Excellent. Try it. */
+            line.open(format);
+            break;
+          } catch (Exception e) {
+            if (line != null) {
+              line.close();
+              line = null;
+            }
+            /* Don't care too much; we'll fall through to the default case
+             * later, and do proper error handling there */
+            Debug.log(Debug.INFO, "mixer: " + mixers[i].getDescription() +
+                " failed: " + e);
+          }
+        }
+      }
+
+      /* If that failed, use the default line. */
+      if (line == null) {
+        line = (SourceDataLine) AudioSystem.getLine(info);
+        line.open(format);
+      }
+    }
+    catch (javax.sound.sampled.LineUnavailableException e) {
+      Debug.error(e.toString());
+      return null;
+    }
+    catch (Exception e) {
+      Debug.error(e.toString());
+      return null;
+    }
+
+    return line;
+  }
+
+  public boolean test() {
+    SourceDataLine line;
+    line = openLine(2, 44000);
+    if (line == null) {
+      return false;
+    }
+    line.close();
+    return true;
+  }
+
+  protected boolean close (RingBuffer ring)
+  {
+    line.stop();
+    line.close();
+
+    return true;
+  }
+
+  protected int write (byte[] data, int offset, int length) {
+    int written = 0;
+	  
+    if ( offset < 0 || offset >= data.length || offset + length > data.length || length <= 0 ) {
+      // This happens on stop for some reason
+      Debug.debug( "Invalid audio write offset=" + offset + ", length=" + length + ", data.length=" + data.length );
+      return length;
+    }
+
+
+    // Need to avoid blocking due to lock contention in line.getFramePosition() in Java 6.
+    while ( true ) {
+      int available = line.available();
+      if ( length > available ) {
+	if ( available > 0 ) {
+	  Debug.debug( "Doing partial audio write of " + available + " bytes" );
+	  written += line.write( data, offset, available );
+	  offset += available;
+	  length -= available;
+	}
+	if ( length > 0 ) {
+	  try {
+	    // Sleep for a quarter of the buffer time before we fill it up again
+	    AudioFormat format = line.getFormat();
+	    long sleepTime = (long)(line.getBufferSize() * 1000
+	      / format.getSampleRate() / format.getSampleSizeInBits() * 8 / (2*channels));//ee
+	    Debug.debug( "Sleeping for " + sleepTime + "ms" );
+	    Thread.sleep(sleepTime);
+	  } catch ( InterruptedException e ) {}
+	  continue;
+	}
+      } else {
+	Debug.debug( "Doing complete audio write of " + length + " bytes" );
+	written += line.write( data, offset, length );
+      }
+      break;
+    }
+    samplesWritten += written / (2 * channels);
+    return written;
+  }
+
+  protected long delay () {
+    int frame;
+    long delay;
+
+    frame = line.getFramePosition();
+    delay = samplesWritten - frame;
+    return delay;
+  }
+
+  protected void reset () {
+    Debug.log(Debug.DEBUG, "reset audio: "+ line);
+    line.flush();
+    samplesWritten = line.getFramePosition();
+    Debug.log(Debug.DEBUG, "samples written: "+ samplesWritten);
+  }
+
+  public String getFactoryName ()
+  {
+    return "audiosinkj2";
+  }
+
+}

Added: trunk/cronus/com/fluendo/plugin/AudioSinkSA.java
===================================================================
--- trunk/cronus/com/fluendo/plugin/AudioSinkSA.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/plugin/AudioSinkSA.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,243 @@
+/* Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.plugin;
+
+import java.io.*;
+import sun.audio.*;
+import com.fluendo.utils.*;
+
+public class AudioSinkSA extends AudioSink
+{
+  private static final int BUFFER = 16 * 1024;
+  private static final int SEGSIZE = 256;
+  private static final int DELAY = 8 * 1000; /* in samples at 8000 Hz */
+
+  private double rateDiff;
+  private int delay;
+
+  private static final boolean ZEROTRAP=true;
+  private static final short BIAS=0x84;
+  private static final int CLIP=32635;
+  private static final byte[] exp_lut =
+    { 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+      4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+      5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+      5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+      6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+      6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+      6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+      6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+      7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+      7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+      7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+      7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+      7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+      7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+      7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+      7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+    };
+
+  /* muLaw header */
+  private static final short[] header =
+                         { 0x2e, 0x73, 0x6e, 0x64,              // header in be
+                           0x00, 0x00, 0x00, 0x18,              // offset
+                           0x7f, 0xff, 0xff, 0xff,              // length
+                           0x00, 0x00, 0x00, 0x01,              // ulaw
+                           0x00, 0x00, 0x1f, 0x40,              // frequency
+                           0x00, 0x00, 0x00, 0x01               // channels
+                         };
+  private int headerPos;
+
+  private final int toUlaw(int sample)
+  {
+    int sign, exponent, mantissa, ulawbyte;
+
+    if (sample>32767) sample=32767;
+    else if (sample<-32768) sample=-32768;
+    /* Get the sample into sign-magnitude. */
+    sign = (sample >> 8) & 0x80;    /* set aside the sign */
+    if (sign != 0) sample = -sample;    /* get magnitude */
+    if (sample > CLIP) sample = CLIP;    /* clip the magnitude */
+
+    /* Convert from 16 bit linear to ulaw. */
+    sample = sample + BIAS;
+    exponent = exp_lut[(sample >> 7) & 0xFF];
+    mantissa = (sample >> (exponent + 3)) & 0x0F;
+    ulawbyte = ~(sign | (exponent << 4) | mantissa);
+    if (ZEROTRAP)
+      if (ulawbyte == 0) ulawbyte = 0x02;  /* optional CCITT trap */
+
+    if (ulawbyte < 0)
+      ulawbyte += 256;
+
+    return ulawbyte;
+  }
+
+  private class RingReader extends InputStream {
+    private AudioStream stream;
+    private RingBufferSA ringBuffer;
+
+    public RingReader(RingBufferSA rb) {
+      ringBuffer = rb;
+      try {
+        headerPos = 0;
+        stream = new AudioStream(this);
+      }
+      catch (Exception e) {
+        e.printStackTrace();
+      }
+    }
+    public synchronized boolean play () {
+      AudioPlayer.player.start(stream);
+      return true;
+    }
+    public synchronized boolean pause () {
+      AudioPlayer.player.stop(stream);
+      return true;
+    }
+    public synchronized boolean stop () {
+      AudioPlayer.player.stop(stream);
+      return true;
+    }
+    public int read() throws IOException {
+      int result;
+      if (headerPos < header.length)
+	result = header[headerPos++];
+      else
+        result = ringBuffer.read();
+ 
+      //System.out.println("read "+result);
+      return result;
+    }
+  }
+
+  private class RingBufferSA extends RingBuffer
+  {
+    private RingReader reader;
+    private int devicePos;
+    public int nextSeg;
+
+    public RingBufferSA () {
+      reader = new RingReader (this);
+      devicePos = 0;
+    }
+
+    protected void startWriteThread () {}
+    public synchronized boolean play () {
+      boolean res;
+      res = super.play();
+      reader.play();
+      return res;
+    }
+    public synchronized boolean pause () {
+      boolean res;
+      res = super.pause();
+      reader.pause();
+      return res;
+    }
+    public synchronized boolean stop () {
+      boolean res;
+      res = super.stop();
+      reader.stop();
+      return res;
+    }
+    public int read () {
+      int ringPos;
+
+      ringPos = (int)(devicePos * rateDiff) * bps;
+
+      /* if we don't know the segment total yet, we need
+       * to return -1 now */
+      if (segTotal == 0)
+        return -1;
+
+      while (ringPos >= nextSeg) {
+        //System.out.println ("read: devicePos: "+devicePos+" ringPos: "+ringPos+" nextSeg: "+nextSeg);
+        synchronized (this) {
+	  clear ((int) (playSeg % segTotal));
+          playSeg++;
+          notifyAll();
+        }
+        nextSeg += segSize;
+      }
+
+      int sample = 0;
+      int ptr = ringPos % buffer.length;
+      for (int j=0; j<channels; j++) {
+        int b1, b2;
+
+        b1 = buffer[ptr  ];
+        b2 = buffer[ptr+1];
+        if (b2<0) b2+=256;
+	/* multiply because we need to keep the sign */
+        sample += (b1 * 256) | b2;
+	ptr += 2;
+      }
+      sample /= channels;
+
+      devicePos++;
+
+      return toUlaw (sample);
+    }
+  }
+
+  protected RingBuffer createRingBuffer() {
+    return new RingBufferSA();
+  }
+
+  protected boolean open (RingBuffer ring) {
+    rateDiff = ring.rate / 8000.0;
+    Debug.log(Debug.INFO, "rateDiff: "+rateDiff);
+
+    ring.segSize = (int) (SEGSIZE * rateDiff);
+    ring.segSize = ring.segSize * ring.bps;
+    ring.segTotal = (int) (BUFFER * rateDiff);
+    ring.segTotal = ring.segTotal * ring.bps / ring.segSize;
+    ring.emptySeg = new byte[ring.segSize];
+    
+    ((RingBufferSA)ring).nextSeg = ring.segSize;
+    delay = DELAY;
+
+    return true;
+  }
+
+  protected boolean close (RingBuffer ring)
+  {
+    return true;
+  }
+
+  protected int write (byte[] data, int offset, int length) {
+    System.out.println("write should not be called");
+    return -1;
+  }
+
+  protected long delay () {
+    long ret = ((int)(delay * rateDiff));
+    return ret;
+  }
+
+  protected void reset () {
+  }
+
+  public String getFactoryName ()
+  {
+    return "audiosinksa";
+  }
+
+}

Added: trunk/cronus/com/fluendo/plugin/FakeSink.java
===================================================================
--- trunk/cronus/com/fluendo/plugin/FakeSink.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/plugin/FakeSink.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,48 @@
+/* Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.plugin;
+
+import com.fluendo.jst.*;
+import com.fluendo.utils.*;
+
+public class FakeSink extends Sink
+{
+  protected int preroll (Buffer buf)
+  {
+    System.out.println("received preroll buffer "+buf);
+    return Pad.OK;
+  }
+
+  protected int render (Buffer buf)
+  {
+    System.out.println("received buffer "+buf);
+    if (buf.object != null) {
+      System.out.println("object "+buf.object);
+    }
+    else {
+      MemUtils.dump (buf.data, 0, buf.length);
+    }
+    return Pad.OK;
+  };
+
+  public String getFactoryName ()
+  {
+    return "fakesink";
+  }
+}

Added: trunk/cronus/com/fluendo/plugin/HTTPSrc.java
===================================================================
--- trunk/cronus/com/fluendo/plugin/HTTPSrc.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/plugin/HTTPSrc.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,431 @@
+/* Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.plugin;
+
+import java.io.*;
+import java.net.*;
+import java.text.MessageFormat;
+import java.util.Locale;
+import com.fluendo.jst.*;
+import com.fluendo.utils.*;
+
+public class HTTPSrc extends Element
+{
+  private String userId;
+  private String password;
+  private String userAgent = "Cortado";
+  private String urlString;
+  private InputStream input;
+  private long contentLength;
+  private long offset;
+  private long offsetLastMessage = 0;
+  private long skipBytes = 0;
+  private String mime;
+  private Caps outCaps;
+  private boolean discont;
+  private URL documentBase;
+  private boolean microSoft = false;
+
+  private static final int DEFAULT_READSIZE = 4096;
+
+  private int readSize = DEFAULT_READSIZE;
+
+  private Pad srcpad = new Pad(Pad.SRC, "src") {
+    private boolean doSeek (Event event) {
+      boolean result;
+      int format;
+      long position;
+
+      format = event.parseSeekFormat();
+      position = event.parseSeekPosition();
+
+      if (format == Format.PERCENT && contentLength != -1) {
+        position = position * contentLength / Format.PERCENT_MAX;
+      }
+      else if (format != Format.BYTES) {
+		  Debug.log (Debug.WARNING, "can only seek in bytes");
+        return false;
+      }
+
+      Debug.log(Debug.DEBUG, this+" flushing");
+      pushEvent (Event.newFlushStart());
+
+      synchronized (streamLock) {
+        Debug.log(Debug.DEBUG, this+" synced");
+
+	result = false;
+        try {
+          input = getInputStream (position);
+	  if (input != null)
+            result = true;
+        }
+        catch (Exception e) {
+	  e.printStackTrace ();
+        }
+        pushEvent (Event.newFlushStop());
+
+        if (result) {
+          pushEvent (Event.newNewsegment(false, Format.BYTES, position, contentLength, position));
+	  postMessage (Message.newStreamStatus (this, true, Pad.OK, "restart after seek"));
+	  result = startTask("cortado-HTTPSrc-Stream-"+Debug.genId());
+	}
+	else {
+	  postMessage (Message.newError (this, "error: Seek failed"));
+	}
+      }
+      return result;
+    }
+
+    protected boolean eventFunc (Event event)
+    {
+      boolean res;
+
+      switch (event.getType()) {
+        case Event.SEEK:
+		res = doSeek(event);
+	  break;
+        default:
+          res = super.eventFunc (event);
+          break;
+      }
+      return res;
+    }
+
+    protected void taskFunc()
+    {
+      int ret;
+      int toRead;
+      long left;
+
+      // Skip to the target offset if required
+      if (skipBytes > 0) {
+	Debug.info("Skipping " + skipBytes + " input bytes");
+	try {
+	 offset += input.skip(skipBytes);
+	} catch (IOException e) {
+	  Debug.error("input.skip error: " + e);
+	  postMessage(Message.newError(this, "File read error"));
+	  return;
+	}
+	skipBytes = 0;
+      }
+
+      // Calculate the read size
+      if (contentLength != -1) {
+        if (microSoft) {
+          /* don't read the last byte in microsoft VM, it screws up the socket
+           * completely. */
+	  if (contentLength == 0)
+	    left = 0;
+          else
+            left = (contentLength - 1) - offset;
+        }
+        else
+          left = contentLength - offset;
+      }
+      else
+	left = -1;
+
+      if (left != -1 && left < readSize)
+	toRead = (int) left;
+      else
+	toRead = readSize;
+
+      // Perform the read
+      Buffer data = Buffer.create();
+      data.ensureSize (toRead);
+      data.offset = 0;
+      try {
+        if (toRead > 0) {
+          data.length = input.read (data.data, 0, toRead);
+	}
+	else {
+          data.length = -1;
+	}
+      }
+      catch (Exception e) {
+        e.printStackTrace();
+        data.length = 0;
+      }
+      if (data.length <= 0) {
+	/* EOS */
+          
+        postMessage (Message.newBytePosition(this, offset));
+        offsetLastMessage = offset;
+          
+        try {
+	  input.close();
+	}
+        catch (Exception e) {
+          e.printStackTrace();
+	}
+	data.close();
+        Debug.log(Debug.INFO, this+" reached EOS");
+	pushEvent (Event.newEOS());
+	postMessage (Message.newStreamStatus (this, false, Pad.UNEXPECTED, "reached EOS"));
+	pauseTask();
+	return;
+      }
+
+      offset += data.length;
+      if(offsetLastMessage > offset) {
+          offsetLastMessage = 0;
+      }
+      if(offset - offsetLastMessage > contentLength / 100) {
+          postMessage (Message.newBytePosition(this, offset));
+          offsetLastMessage = offset;
+      }
+
+      // Negotiate capabilities
+      if (srcpad.getCaps() == null) {
+	String typeMime;
+
+	typeMime = ElementFactory.typeFindMime (data.data, data.offset, data.length);
+	if (typeMime != null) {
+	  if (!typeMime.equals (mime)) {
+	    Debug.log(Debug.WARNING, "server contentType: "+mime+" disagrees with our typeFind: "
+		       +typeMime);
+	  }
+	  Debug.log(Debug.INFO, "using typefind contentType: "+typeMime);
+	  mime = typeMime;
+	}
+	else {
+	  Debug.log(Debug.INFO, "typefind failed, using server contentType: "+mime);
+	}
+
+	outCaps = new Caps (mime);
+	srcpad.setCaps (outCaps);
+      }
+
+      data.caps = outCaps;
+      data.setFlag (com.fluendo.jst.Buffer.FLAG_DISCONT, discont);
+      discont = false;
+
+      // Push the data to the peer
+      if ((ret = push(data)) != OK) {
+	if (isFlowFatal(ret) || ret == Pad.NOT_LINKED) {
+	  postMessage (Message.newError (this, "error: "+getFlowName (ret)));
+	  pushEvent (Event.newEOS());
+	}
+	postMessage (Message.newStreamStatus (this, false, ret, "reason: "+getFlowName (ret)));
+	pauseTask();
+      }
+    }
+    
+    protected boolean activateFunc (int mode)
+    {
+      boolean res = true;
+
+      switch (mode) {
+        case MODE_NONE:
+	  postMessage (Message.newStreamStatus (this, false, Pad.WRONG_STATE, "stopping"));
+	  res = stopTask();
+	  input = null;
+	  outCaps = null;
+	  mime = null;
+	  break;
+        case MODE_PUSH:
+	  try {
+	    contentLength = -1;
+	    input = getInputStream(0);
+	    if (input == null)
+	      res = false;
+	  }
+	  catch (Exception e) {
+	    res = false;
+	  }
+	  if (res) {
+	    postMessage (Message.newStreamStatus (this, true, Pad.OK, "activating"));
+	    res = startTask("cortado-HTTPSrc-Stream-"+Debug.genId());
+	  }
+	  break;
+	default:
+	  res = false;
+	  break;
+      }
+      return res;
+    }
+  };
+
+  private InputStream openWithConnection(URL url, long offset) throws IOException
+  {
+    InputStream dis = null;
+
+    URLConnection uc = url.openConnection();
+
+    uc.setRequestProperty ("Connection", "Keep-Alive");
+
+    String range;
+    if (offset != 0 && contentLength != -1)
+      range = "bytes=" + offset+"-"+(contentLength-1);
+    else if (offset != 0)
+      range = "bytes=" + offset+"-";
+    else
+      range = null;
+    if (range != null) {
+      Debug.info("doing range: "+range);
+      uc.setRequestProperty ("Range", range);
+    }
+
+    uc.setRequestProperty ("User-Agent", userAgent);
+    if (userId != null && password != null) {
+      String userPassword = userId + ":" + password;
+      String encoding = Base64Converter.encode (userPassword.getBytes());
+      uc.setRequestProperty ("Authorization", "Basic " + encoding);
+    }
+    uc.setRequestProperty ("Content-Type","application/octet-stream");
+
+    /* This will send the request. */
+    dis = uc.getInputStream();
+
+    String responseRange = uc.getHeaderField("Content-Range");
+    long responseOffset;
+    if (responseRange == null) {
+      Debug.info("Response contained no Content-Range field, assuming offset=0");
+      responseOffset = 0;
+    } else {
+      try {
+	MessageFormat format = new MessageFormat("bytes {0,number}-{1,number}");
+        format.setLocale(Locale.US);
+	java.lang.Object parts[] = format.parse(responseRange);
+	responseOffset = ((Number)parts[0]).longValue();
+	if (responseOffset < 0) {
+	  responseOffset = 0;
+	}
+	Debug.debug("Stream successfully with offset " + responseOffset);
+      } catch (Exception e) {
+	Debug.info("Error parsing Content-Range header");
+	responseOffset = 0;
+      }
+    }
+
+    contentLength = uc.getHeaderFieldInt ("Content-Length", -1) + responseOffset;
+    mime = uc.getContentType();
+    this.offset = responseOffset;
+
+    if (responseOffset < offset) {
+      this.skipBytes = offset - responseOffset;
+    } else {
+      this.skipBytes = 0;
+    }
+
+    return dis;
+  }
+
+  private InputStream getInputStream (long offset) throws Exception
+  {
+    InputStream dis = null;
+	  
+    try {
+      URL url;
+      boolean isAbsolute;
+
+      postMessage(Message.newResource (this, "Opening "+urlString));
+      Debug.log(Debug.INFO, "reading from url "+urlString);
+		
+      /* IE fails parsing absolute urls in an absolute context; it adds some
+       * random slashes. We workaround this by checking if the urlString is
+       * absolute and avoid the documentBase parsing */
+      isAbsolute = urlString.startsWith("http://");
+		
+      if (!isAbsolute && documentBase != null) {
+        Debug.log(Debug.INFO, "parsing in document base");
+        url = new URL(documentBase, urlString);
+      }
+      else {
+        Debug.log(Debug.INFO, "parsing as absolute URL");
+        url = new URL(urlString);
+      }
+
+      Debug.log(Debug.INFO, "trying to open "+url+" at offset "+offset);
+
+      dis = openWithConnection(url, offset);
+		
+      discont = true;
+
+      if (contentLength != -1) {
+        postMessage(Message.newDuration (this, Format.BYTES, contentLength));
+      }
+
+      Debug.log(Debug.INFO, "opened "+url);
+      Debug.log(Debug.INFO, "contentLength: "+contentLength);
+      Debug.log(Debug.INFO, "server contentType: "+mime);
+    }
+    catch (SecurityException e) {
+      e.printStackTrace();
+      postMessage(Message.newError (this, "Not allowed "+urlString+"..."));
+    }
+    catch (Exception e) {
+		  try{
+        		dis=new FileInputStream(System.getProperty("user.dir")
+            	+System.getProperty("file.separator")+urlString);
+      		}
+      		catch(Exception ee){
+				ee.printStackTrace();
+      			postMessage(Message.newError (this, "Failed opening "+urlString+"..."));
+			}
+    }
+	  catch (Throwable t) {
+      t.printStackTrace();
+      postMessage(Message.newError (this, "Failed opening "+urlString+"..."));
+    }
+
+    return dis;
+  }
+
+  public String getFactoryName () {
+    return "httpsrc";
+  }
+
+  public HTTPSrc () {
+    super ();
+    if (System.getProperty("java.vendor").toUpperCase().startsWith ("MICROSOFT", 0)) {
+      Debug.log (Debug.WARNING, "Found MS JVM, work around inputStream EOS bugs.");
+      microSoft = true;
+    }
+    addPad (srcpad);
+  }
+
+  public synchronized boolean setProperty(String name, java.lang.Object value) {
+    boolean res = true;
+
+    if (name.equals("url")) {
+      urlString = String.valueOf(value);
+    }
+    else if (name.equals("documentBase")) {
+      documentBase = (URL)value;
+    }
+    else if (name.equals("userId")) {
+      userId = (value == null) ? null : String.valueOf(value);
+    }
+    else if (name.equals("userAgent")) {
+      userAgent = String.valueOf(value);
+    }
+    else if (name.equals("password")) {
+      password = (value == null) ? null : String.valueOf(value);
+    }
+    else if (name.equals("readSize")) {
+      readSize = Integer.parseInt((String)value);
+    }
+    else {
+      res = false;
+    }
+    return res;
+  }
+}

Added: trunk/cronus/com/fluendo/plugin/JPEGDec.java
===================================================================
--- trunk/cronus/com/fluendo/plugin/JPEGDec.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/plugin/JPEGDec.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,166 @@
+/* Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.plugin;
+
+import java.awt.*;
+import com.fluendo.jst.*;
+import com.fluendo.utils.*;
+
+public class JPEGDec extends Element
+{
+  private Toolkit toolkit;
+  private Component component;
+  private MediaTracker mediaTracker;
+  private int width, height;
+
+  private Pad srcpad = new Pad(Pad.SRC, "src") {
+    protected boolean eventFunc (com.fluendo.jst.Event event) {
+      return sinkpad.pushEvent(event);
+    }
+  };
+
+  private Pad sinkpad = new Pad(Pad.SINK, "sink") {
+    protected boolean eventFunc (com.fluendo.jst.Event event) {
+      boolean result;
+
+      switch (event.getType()) {
+        case com.fluendo.jst.Event.FLUSH_START:
+          result = srcpad.pushEvent (event);
+          synchronized (streamLock) {
+            Debug.log(Debug.INFO, "synced "+this);
+          }
+          break;
+        case com.fluendo.jst.Event.FLUSH_STOP:
+          result = srcpad.pushEvent(event);
+          break;
+        case com.fluendo.jst.Event.EOS:
+        case com.fluendo.jst.Event.NEWSEGMENT:
+        default:
+          result = srcpad.pushEvent(event);
+          break;
+      }
+      return result;
+    }
+    protected int chainFunc (com.fluendo.jst.Buffer buf) {
+      int ret;
+      Image img = null;
+
+      img = toolkit.createImage(buf.data, buf.offset, buf.length);
+      if (img != null) {
+        int imgWidth, imgHeight;
+	 
+        try {
+          mediaTracker.addImage(img, 0);
+          mediaTracker.waitForID(0);
+          mediaTracker.removeImage(img, 0);
+        }
+        catch (Exception e) {
+          e.printStackTrace();
+          return Pad.ERROR;
+        }
+
+	imgWidth = img.getWidth(null);
+	imgHeight = img.getHeight(null);
+
+        if (imgWidth != width || imgHeight != height) {
+	  width = imgWidth;
+	  height = imgHeight;
+
+          Debug.log(Debug.INFO, "jpeg frame: "+width+","+height);
+
+          caps = new Caps ("video/raw");
+          caps.setFieldInt ("width", width);
+          caps.setFieldInt ("height", height);
+          caps.setFieldInt ("aspect_x", 1);
+          caps.setFieldInt ("aspect_y", 1);
+	}
+        buf.object = img;
+        buf.caps = caps;
+
+        ret = srcpad.push(buf);
+      }
+      else {
+	System.out.println ("could not decode jpeg image");
+	Debug.log (Debug.WARNING, "could not decode jpeg image, continueing");
+        buf.close();
+	ret = OK;
+      }
+      return ret;
+    }
+  };
+
+  public JPEGDec() {
+    super();
+
+    toolkit = Toolkit.getDefaultToolkit();
+
+    addPad (srcpad);
+    addPad (sinkpad);
+  }
+
+  protected int changeState (int transition) {
+    int res;
+
+    switch (transition) {
+      case STOP_PAUSE:
+        width = -1;
+        height = -1;
+        break;
+      default:
+        break;
+    }
+
+    res = super.changeState (transition);
+
+    return res;
+  }
+
+  public boolean setProperty (String name, java.lang.Object value) {
+    if (name.equals("component")) {
+      component = (Component) value;
+      toolkit = component.getToolkit();
+      mediaTracker = new MediaTracker (component);
+    }
+    else
+      return false;
+
+    return true;
+  }
+  public java.lang.Object getProperty (String name) {
+    if (name.equals("component")) {
+      return component;
+    }
+    return null;
+  }
+
+
+  public String getFactoryName ()
+  {
+    return "jpegdec";
+  }
+  public String getMime ()
+  {
+    return "image/jpeg";
+  }
+  public int typeFind (byte[] data, int offset, int length)
+  {
+    return -1;
+  }
+
+}

Added: trunk/cronus/com/fluendo/plugin/KateDec.java
===================================================================
--- trunk/cronus/com/fluendo/plugin/KateDec.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/plugin/KateDec.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,346 @@
+/* Copyright (C) <2008> ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ * based on code Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.plugin;
+
+import java.util.*;
+import org.xiph.ogg.*;
+import com.fluendo.jkate.*;
+import com.fluendo.jst.*;
+import com.fluendo.utils.*;
+
+/**
+ * Katedec is a decoder element for the Kate stream format.
+ * See http://wiki.xiph.org/index.php/OggKate for more information.
+ * Kate streams may be multiplexed in Ogg.
+ * The Katedec element accepts Kate packets (presumably demultiplexed by an
+ * Ogg demuxer element) on its sink, and generates Kate events on its source.
+ * Kate events are Kate specific structures, which may then be interpreted
+ * by a renderer.
+*/
+public class KateDec extends Element implements OggPayload
+{
+  /* Kate magic: 0x80 (BOS header) followed by "kate\0\0\0" */
+  private static final byte[] signature = { -128, 0x6b, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00 };
+
+  private Info ki;
+  private Comment kc;
+  private State k;
+  private Packet op;
+  private int packetno;
+
+  private long basetime = 0;
+  private long lastTs;
+  private boolean haveDecoder = false;
+
+  /*
+   * OggPayload interface
+   */
+  public boolean isType (Packet op)
+  {
+    return typeFind (op.packetBase, op.packet, op.bytes) > 0;
+  }
+  public boolean isKeyFrame (Packet op)
+  {
+    return true;
+  }
+
+  /**
+   * A discontinuous codec will not cause the pipeline to wait for data if starving
+   */
+  public boolean isDiscontinuous ()
+  {
+    return true;
+  }
+  public int takeHeader (Packet op)
+  {
+	int ret = ki.decodeHeader(kc, op);
+    if (ret > 0) {
+      k.decodeInit(ki);
+      Debug.debug("Kate decoder ready");
+      haveDecoder = true;
+    }
+    return ret;
+  }
+  public boolean isHeader (Packet op)
+  {
+    return (op.packetBase[op.packet] & 0x80) == 0x80;
+  }
+  public long getFirstTs (Vector packets)
+  {
+    int len = packets.size();
+    int i;
+    long time;
+    com.fluendo.jst.Buffer data = null;
+
+    /* first find buffer with valid offset */
+    for (i=0; i<len; i++) {
+      data = (com.fluendo.jst.Buffer) packets.elementAt(i);
+
+      if (data.time_offset != -1)
+        break;
+    }
+    if (i == packets.size())
+      return -1;
+
+    time = granuleToTime (data.time_offset);
+
+    data = (com.fluendo.jst.Buffer) packets.elementAt(0);
+    data.timestamp = time - (long) ((i+1) * (Clock.SECOND * ki.gps_denominator / ki.gps_numerator));
+
+    return time;
+  }
+
+  /**
+   * Converts a granule position to its time equivalent
+  */
+  public long granuleToTime (long gp)
+  {
+    long res;
+
+    if (gp < 0 || !haveDecoder)
+      return -1;
+
+    res = (long) (k.granuleTime(gp) * Clock.SECOND);
+
+    return res;
+  }
+
+  /**
+   * Converts a granule position to its duration equivalent
+   */
+  public long granuleToDuration (long gp)
+  {
+    long res;
+
+    if (gp < 0 || !haveDecoder)
+      return -1;
+
+    res = (long) (k.granuleDuration(gp) * Clock.SECOND);
+
+    return res;
+  }
+
+  private Pad srcPad = new Pad(Pad.SRC, "src") {
+    protected boolean eventFunc (com.fluendo.jst.Event event) {
+      return sinkPad.pushEvent(event);
+    }
+  };
+
+  private Pad sinkPad = new Pad(Pad.SINK, "sink") {
+    protected boolean eventFunc (com.fluendo.jst.Event event) {
+      boolean result;
+
+      switch (event.getType()) {
+        case com.fluendo.jst.Event.FLUSH_START:
+	  result = srcPad.pushEvent (event);
+	  synchronized (streamLock) {
+            Debug.log(Debug.DEBUG, "synced "+this);
+	  }
+          break;
+        case com.fluendo.jst.Event.FLUSH_STOP:
+          result = srcPad.pushEvent(event);
+          break;
+        case com.fluendo.jst.Event.EOS:
+          Debug.log(Debug.INFO, "got EOS "+this);
+          result = srcPad.pushEvent(event);
+          break;
+        case com.fluendo.jst.Event.NEWSEGMENT:
+          basetime = event.parseNewsegmentStart();
+          Debug.info("new segment: base time "+basetime);
+          result = srcPad.pushEvent(event);
+          break;
+	default:
+          result = srcPad.pushEvent(event);
+          break;
+      }
+      return result;
+    }
+
+    /**
+     * receives Kate packets, and generates Kate events
+     */
+    protected int chainFunc (com.fluendo.jst.Buffer buf) {
+      int result;
+      long timestamp;
+
+      Debug.log( Debug.DEBUG, parent.getName() + " <<< " + buf );
+
+      op.packetBase = buf.data;
+      op.packet = buf.offset;
+      op.bytes = buf.length;
+      op.bos = (packetno == 0 ? 1 : 0);
+      op.eos = 0;
+      op.packetno = packetno;
+      timestamp = buf.timestamp;
+
+      Debug.log(Debug.DEBUG, "Kate chainFunc with packetno "+packetno+", haveDecoder "+haveDecoder);
+
+//      if (buf.isFlagSet (com.fluendo.jst.Buffer.FLAG_DISCONT)) {
+//        Debug.log(Debug.INFO, "kate: got discont");
+//        /* should flush, if we keep events to handle repeats in the future */
+//        lastTs = -1;
+//      }
+
+      if (!haveDecoder) {
+        //System.out.println ("decoding header");
+        result = takeHeader(op);
+        if (result < 0){
+          buf.close();
+          // error case; not a kate header
+          Debug.log(Debug.ERROR, "does not contain Kate data.");
+          return ERROR;
+        }
+        else if (result > 0) {
+          // we've decoded all headers
+          Debug.log(Debug.DEBUG, "Kate initialized for decoding");
+
+          /* we're sending raw kate_event structures */
+	  caps = new Caps ("application/x-kate-event");
+        }
+        buf.close();
+        packetno++;
+
+	return OK;
+      }
+      else {
+        if ((op.packetBase[op.packet] & 0x80) == 0x80) {
+          Debug.log(Debug.DEBUG, "ignoring header");
+          buf.close();
+          return OK;
+        }
+
+	if (timestamp != -1) {
+	  lastTs = timestamp;
+	}
+
+	if (true) {
+	  try{
+            result = k.decodePacketin(op);
+            if (result < 0) {
+              buf.close();
+              Debug.log(Debug.ERROR, "Error Decoding Kate.");
+	      postMessage (Message.newError (this, "Error decoding Kate"));
+              return ERROR;
+            }
+            com.fluendo.jkate.Event ev = k.decodeEventOut();
+            if (ev != null) {
+              buf.object = ev;
+	      buf.caps = caps;
+	      buf.timestamp = granuleToDuration(ev.start);
+	      buf.timestampEnd = buf.timestamp + granuleToDuration(ev.duration);
+              Debug.log( Debug.DEBUG, parent.getName() + " >>> " + buf );
+              Debug.debug("Got Kate text: "+new String(ev.text)+" from "+buf.timestamp+" to "+buf.timestampEnd+", basetime "+basetime);
+              result = srcPad.push(buf);
+              Debug.log(Debug.DEBUG, "push returned "+result);
+            }
+            else {
+              Debug.debug("Got no event");
+	      buf.close();
+              result = OK;
+            }
+          }
+	  catch (Exception e) {
+	    e.printStackTrace();
+	    postMessage (Message.newError (this, e.getMessage()));
+            result = ERROR;
+	  }
+	}
+        else {
+          result = OK;
+	  buf.close();
+	}
+      }
+      packetno++;
+
+      return result;
+    }
+
+    protected boolean activateFunc (int mode)
+    {
+      return true;
+    }
+  };
+
+  public KateDec() {
+    super();
+
+    ki = new Info();
+    kc = new Comment();
+    k = new State();
+    op = new Packet();
+
+    addPad (srcPad);
+    addPad (sinkPad);
+  }
+
+  protected int changeState (int transition) {
+    int res;
+
+    switch (transition) {
+      case STOP_PAUSE:
+        lastTs = -1;
+        packetno = 0;
+	break;
+      default:
+        break;
+    }
+
+    res = super.changeState (transition);
+
+    switch (transition) {
+      case PAUSE_STOP:
+	ki.clear();
+	kc.clear();
+	k.clear();
+	break;
+      default:
+        break;
+    }
+
+    return res;
+  }
+
+  public java.lang.Object getProperty (String name) {
+    if (name.equals("language")) {
+      return ki.language;
+    }
+    else if (name.equals("category")) {
+      return ki.category;
+    }
+    else {
+      return super.getProperty(name);
+    }
+  }
+
+  public String getFactoryName ()
+  {
+    return "katedec";
+  }
+  public String getMime ()
+  {
+    return "application/x-kate";
+  }
+  public int typeFind (byte[] data, int offset, int length)
+  {
+    if (MemUtils.startsWith (data, offset, length, signature))
+      return 10;
+    return -1;
+  }
+}

Added: trunk/cronus/com/fluendo/plugin/KateOverlay.java
===================================================================
--- trunk/cronus/com/fluendo/plugin/KateOverlay.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/plugin/KateOverlay.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,104 @@
+/* Copyright (C) <2008> ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.plugin;
+
+import java.awt.*;
+import java.awt.image.*;
+import com.fluendo.jst.*;
+import com.fluendo.jtiger.Renderer;
+import com.fluendo.utils.*;
+
+/* This element renders a Kate stream on incoming video */
+public class KateOverlay extends Overlay
+{
+  private Font font = null;
+  private String text = null;
+  private Renderer tr = new Renderer();
+
+  private Pad kateSinkPad = new Pad(Pad.SINK, "katesink") {
+    protected boolean eventFunc (com.fluendo.jst.Event event) {
+      /* don't propagate, the video sink is the master */
+      return true;
+    }
+
+    /**
+     * This pad receives Kate events, and add them to the renderer.
+     * They will be removed from it as they become inactive.
+     */
+    protected synchronized int chainFunc (com.fluendo.jst.Buffer buf) {
+      addKateEvent((com.fluendo.jkate.Event)buf.object);
+      return Pad.OK;
+    }
+  };
+
+  /**
+   * Create a new Kate overlay
+   */
+  public KateOverlay() {
+    super();
+    addPad(kateSinkPad);
+  }
+
+
+  /**
+   * Add a new Kate event to the renderer.
+   * This needs locking so the Kate events are not changed while the
+   * overlay is rendering them to an image.
+   */
+  protected synchronized void addKateEvent(com.fluendo.jkate.Event ev) {
+    tr.add(ev);
+    Debug.log(Debug.DEBUG, "Kate overlay got Kate event: "+new String(ev.text));
+  }
+
+  /**
+   * Overlay the Kate renderer onto the given image.
+   */
+  protected synchronized void overlay(com.fluendo.jst.Buffer buf) {
+    Image img;
+
+    if (buf.object instanceof ImageProducer) {
+      img = component.createImage((ImageProducer)buf.object);
+    }
+    else if (buf.object instanceof Image) {
+      img = (Image)buf.object;
+    }
+    else {
+      System.out.println(this+": unknown buffer received "+buf);
+      return;
+    }
+
+    /* before rendering, we update the state of the events; for now this
+       just weeds out old ones, but at some point motions could be tracked. */
+    int ret = tr.update(component, img, buf.timestamp/(double)Clock.SECOND);
+    /* if there are no Kate events active, just return the buffer as is */
+    if (ret == 1)
+      return;
+
+    /* render Kate stream on top */
+    img = tr.render(component, img);
+
+    buf.object = img;
+  }
+
+  public String getFactoryName ()
+  {
+    return "kateoverlay";
+  }
+}
+

Added: trunk/cronus/com/fluendo/plugin/MulawDec.java
===================================================================
--- trunk/cronus/com/fluendo/plugin/MulawDec.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/plugin/MulawDec.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,98 @@
+/* Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.plugin;
+
+import com.fluendo.jst.*;
+import com.fluendo.utils.*;
+
+public class MulawDec extends Element 
+{
+  private int rate, channels;
+
+  private Pad srcPad = new Pad(Pad.SRC, "src") {
+    protected boolean eventFunc (com.fluendo.jst.Event event) {
+      return sinkPad.pushEvent(event);
+    }
+  };
+
+  private Pad sinkPad = new Pad(Pad.SINK, "sink") {
+    protected boolean eventFunc (com.fluendo.jst.Event event) {
+      boolean result;
+
+      switch (event.getType()) {
+        case com.fluendo.jst.Event.FLUSH_START:
+          result = srcPad.pushEvent (event);
+          synchronized (streamLock) {
+            Debug.log(Debug.INFO, "synced "+this);
+          }
+          break;
+        case com.fluendo.jst.Event.FLUSH_STOP:
+          result = srcPad.pushEvent(event);
+          break;
+        case com.fluendo.jst.Event.EOS:
+        case com.fluendo.jst.Event.NEWSEGMENT:
+        default:
+          result = srcPad.pushEvent(event);
+          break;
+      }
+      return result;
+    }
+    protected int chainFunc (com.fluendo.jst.Buffer buf) {
+      int ret;
+
+      if (caps == null) {
+        Debug.log(Debug.INFO, "mulaw: rate: "+rate);
+        Debug.log(Debug.INFO, "mulaw: channels: "+channels);
+
+        caps = new Caps ("audio/x-mulaw");
+        caps.setFieldInt ("rate", rate);
+        caps.setFieldInt ("channels", channels);
+      }
+
+      buf.caps = caps;
+
+      ret = srcPad.push(buf);
+
+      return ret;
+    }
+  };
+
+  public MulawDec() {
+    super();
+
+    rate = 8000;    
+    channels = 1;    
+
+    addPad (srcPad);
+    addPad (sinkPad);
+  }
+
+  public String getFactoryName ()
+  {
+    return "mulawdec";
+  }
+  public String getMime ()
+  {
+    return "audio/x-mulaw";
+  }
+  public int typeFind (byte[] data, int offset, int length)
+  {
+    return -1;
+  }
+}

Added: trunk/cronus/com/fluendo/plugin/MultipartDemux.java
===================================================================
--- trunk/cronus/com/fluendo/plugin/MultipartDemux.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/plugin/MultipartDemux.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,368 @@
+/* Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.plugin;
+
+import java.util.*;
+import com.fluendo.jst.*;
+import com.fluendo.utils.*;
+
+public class MultipartDemux extends Element {
+    private static final String MIME = "multipart/x-mixed-replace";
+
+    private static final String DEFAULT_BOUNDARY = "--ThisRandomString";
+
+    private Vector streams;
+
+    private byte[] accum;
+
+    private int accumSize;
+
+    private int accumPos;
+
+    private int dataEnd;
+
+    private static final int STATE_FIND_BOUNDARY = 1;
+
+    private static final int STATE_PARSE_HEADERS = 2;
+
+    private static final int STATE_FIND_DATA_END = 3;
+
+    private int state = STATE_FIND_BOUNDARY;
+
+    private String boundaryString = DEFAULT_BOUNDARY;
+
+    private byte[] boundary = boundaryString.getBytes();
+
+    private int boundaryLen = boundary.length;
+
+    private static final byte[] headerEnd = "\n".getBytes();
+
+    private static final int headerEndLen = headerEnd.length;
+
+    private static final String contentType = "content-type: ";
+
+    private static final int contentTypeLen = contentType.length();
+
+    private MultipartStream currentStream = null;
+
+    class MultipartStream extends Pad {
+        private String mimeType;
+
+        public MultipartStream(String mime) {
+            super(Pad.SRC, "src_" + mime);
+
+            mimeType = mime;
+            caps = new Caps(mime);
+        }
+
+        protected boolean eventFunc(com.fluendo.jst.Event event) {
+            return sinkpad.pushEvent(event);
+        }
+    }
+
+    private Pad sinkpad = new Pad(Pad.SINK, "sink") {
+
+        protected boolean setCapsFunc(Caps caps) {
+            String mime = caps.getMime();
+            String capsBoundary;
+
+            if (!mime.equals(MIME)) {
+                postMessage(Message.newError(this, "expected \"" + mime
+                        + "\", got \"" + mime + "\""));
+                return false;
+            }
+
+            capsBoundary = caps.getFieldString("boundary", DEFAULT_BOUNDARY);
+
+            Debug.log(Debug.INFO, this + " boundary string: \"" + capsBoundary
+                    + "\"");
+
+            boundaryString = capsBoundary + "\n";
+            boundary = boundaryString.getBytes();
+            boundaryLen = boundary.length;
+
+            return true;
+        }
+
+        private MultipartStream findStream(String mime) {
+            MultipartStream stream = null;
+            for (int i = 0; i < streams.size(); i++) {
+                stream = (MultipartStream) streams.elementAt(i);
+                if (stream.mimeType.equals(mime))
+                    break;
+                stream = null;
+            }
+            return stream;
+        }
+
+        private boolean forwardEvent(com.fluendo.jst.Event event) {
+            for (int i = 0; i < streams.size(); i++) {
+                MultipartStream stream = (MultipartStream) streams.elementAt(i);
+                stream.pushEvent(event);
+            }
+            return true;
+        }
+
+        protected boolean eventFunc(com.fluendo.jst.Event event) {
+            switch (event.getType()) {
+            case Event.FLUSH_START:
+                forwardEvent(event);
+                synchronized (streamLock) {
+                    Debug.log(Debug.INFO, "synced " + this);
+                }
+                break;
+            case Event.NEWSEGMENT:
+            case Event.FLUSH_STOP:
+            case Event.EOS:
+                synchronized (streamLock) {
+                    forwardEvent(event);
+                }
+                break;
+            default:
+                forwardEvent(event);
+                break;
+            }
+            return true;
+        }
+
+        /*
+         * copy the buffer data into our buffer. If we need to enlarge the
+         * buffer, we can flush out any skipped bytes
+         */
+        private void accumulateBuffer(com.fluendo.jst.Buffer buf) {
+            int lastPos = accumSize + accumPos;
+
+            /* make room */
+            if (accum.length < lastPos + buf.length) {
+                byte[] newAcum;
+
+                /*
+                 * FIXME, check if we need a bigger buffer or if we can just
+                 * shrink the current one
+                 */
+                newAcum = new byte[accum.length + buf.length];
+                System.arraycopy(accum, accumPos, newAcum, 0, accumSize);
+                accum = newAcum;
+                accumPos = 0;
+                lastPos = accumSize;
+            }
+            System.arraycopy(buf.data, buf.offset, accum, lastPos, buf.length);
+            accumSize += buf.length;
+            // System.out.println("added "+buf.length+" pos "+accumPos+" size
+            // "+accumSize);
+        }
+
+        private void flushBytes(int bytes) {
+            accumPos += bytes;
+            accumSize -= bytes;
+            // System.out.println("flushing "+bytes+" pos "+accumPos+" size
+            // "+accumSize);
+        }
+
+        /*
+         * find bytes of consecutive bytes in the buffer. This function returns
+         * the position in the buffer where the bytes were found or -1 if the
+         * bytes were not found.
+         */
+        private int findBytes(int startPos, byte[] bytes, int bytesLen) {
+            /*
+             * startPos is the first byte in the buffer where we should start
+             * scanning.
+             */
+            int scanPos = startPos;
+            /* we always start comparing the first byte of the bytes */
+            int pos = 0;
+            int size = accumSize;
+
+            // System.out.println("findBytes: size:"+size+" "+bytesLen);
+            /* while we have enough data to compare */
+            while (size > bytesLen) {
+                /* check if we have a match */
+                if (accum[scanPos] == bytes[pos]) {
+                    // System.out.println("match: scanPos:"+scanPos+"
+                    // "+accum[scanPos]+" pos:"+pos+" "+bytes[pos]);
+                    /* position to compare the next byte */
+                    pos++;
+                    if (pos == bytesLen) {
+                        /*
+                         * we found all consecutive bytes, we have a complete
+                         * match
+                         */
+                        return startPos;
+                    }
+                } else {
+                    /*
+                     * we have no match, flush our buffer to next byte and start
+                     * scanning for the first byte in the bytes string again.
+                     * The data size decrements.
+                     */
+                    // System.out.println("fail: scanPos:"+scanPos+"
+                    // "+accum[scanPos]+" pos:"+pos+" "+bytes[pos]);
+                    scanPos -= pos;
+                    size += pos;
+                    startPos = scanPos + 1;
+                    pos = 0;
+                }
+                /* move to next byte */
+                scanPos++;
+                size--;
+            }
+            return -1;
+        }
+
+        /*
+         * find boundary bytes of consecutive bytes in the buffer. This function
+         * returns true if the bytes where found with the accumPos position
+         * pointing to the byte in the buffer.
+         */
+        private boolean findBoundary() {
+            int pos = findBytes(accumPos, boundary, boundaryLen);
+            if (pos != -1) {
+                flushBytes(pos - accumPos);
+                // System.out.println("buffer now: sync at "+accumPos);
+            }
+            return pos != -1;
+        }
+
+        /*
+         * read the headers up to the first \n\n sequence. we store the
+         * Content-Type: header in lastContentType
+         */
+        private boolean parseHeaders() {
+            int headerStart = accumPos;
+            int prevHdr;
+
+            while (true) {
+                prevHdr = headerStart;
+
+                int pos = findBytes(headerStart, headerEnd, headerEndLen);
+                if (pos == -1)
+                    return false;
+
+                if (pos == prevHdr) {
+                    /* all headers parsed */
+                    flushBytes(pos + 1 - accumPos);
+                    return true;
+                }
+                String header = new String(accum, headerStart, pos
+                        - headerStart);
+                header = header.toLowerCase();
+
+                if (header.startsWith(contentType)) {
+                    String mime = header.substring(contentTypeLen).trim();
+
+                    currentStream = findStream(mime);
+                    if (currentStream == null) {
+                        currentStream = new MultipartStream(mime);
+                        streams.addElement(currentStream);
+                        addPad(currentStream);
+                    }
+                }
+
+                /* go to next header */
+                headerStart = pos + 1;
+            }
+        }
+
+        private boolean findDataEnd() {
+            int pos = findBytes(accumPos, boundary, boundaryLen);
+            // System.out.println("find data end "+accumPos+" size "+accumSize+"
+            // pos "+pos);
+            if (pos != -1) {
+                dataEnd = pos - 1;
+            }
+            return pos != -1;
+        }
+
+        protected int chainFunc(com.fluendo.jst.Buffer buf) {
+            int flowRet = OK;
+
+            // System.out.println("input");
+            // MemUtils.dump (buf.data, buf.offset, buf.length);
+
+            accumulateBuffer(buf);
+            buf.close();
+
+            switch (state) {
+            case STATE_FIND_BOUNDARY:
+                if (!findBoundary())
+                    break;
+                /* skip boundary */
+                flushBytes(boundary.length);
+                state = STATE_PARSE_HEADERS;
+            /* fallthrough */
+            case STATE_PARSE_HEADERS:
+                if (!parseHeaders())
+                    break;
+                state = STATE_FIND_DATA_END;
+            /* fallthrough */
+            case STATE_FIND_DATA_END:
+                if (!findDataEnd())
+                    break;
+
+                com.fluendo.jst.Buffer data = com.fluendo.jst.Buffer.create();
+                int dataSize = dataEnd - accumPos;
+
+                // System.out.println("dataSize: "+dataSize);
+                // MemUtils.dump (accum, accumPos, dataSize);
+
+                data.copyData(accum, accumPos, dataSize);
+                data.time_offset = -1;
+                data.timestamp = -1;
+
+                /* skip data */
+                flushBytes(dataSize);
+
+                /* and push */
+                flowRet = currentStream.push(data);
+                state = STATE_FIND_BOUNDARY;
+                break;
+            default:
+                flowRet = ERROR;
+                break;
+            }
+            // System.out.println("return "+flowRet);
+            return flowRet;
+        }
+    };
+
+    public String getFactoryName() {
+        return "multipartdemux";
+    }
+
+    public String getMime() {
+        return MIME;
+    }
+
+    public int typeFind(byte[] data, int offset, int length) {
+        return -1;
+    }
+
+    public MultipartDemux() {
+        super();
+
+        accum = new byte[8192];
+        accumSize = 0;
+        accumPos = 0;
+
+        streams = new Vector();
+
+        addPad(sinkpad);
+    }
+}

Added: trunk/cronus/com/fluendo/plugin/OggDemux.java
===================================================================
--- trunk/cronus/com/fluendo/plugin/OggDemux.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/plugin/OggDemux.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,605 @@
+/* Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.plugin;
+
+import java.util.*;
+import org.xiph.ogg.*;
+import com.meviatronic.zeus.pollux.*;
+import com.meviatronic.zeus.castor.*;
+import com.fluendo.jst.*;
+import com.fluendo.utils.*;
+
+public final class OggDemux extends Element
+{
+  private SyncState oy;
+  private OggChain chain;
+  private Page og;
+  private Packet op;
+  private static final byte[] signature = { 0x4f, 0x67, 0x67, 0x53 };
+  private static final byte[] fishead_signature = { 0x66, 0x69, 0x73, 0x68, 0x65, 0x61, 0x64};
+  private static final byte[] cmml_signature = {0x43, 0x4d, 0x4d, 0x4c};
+
+  private static final int TYPE_NEW = 0;
+  private static final int TYPE_UNKNOWN = 1;
+  private static final int TYPE_SKELETON = 2;
+  private static final int TYPE_CMML = 3;
+  private static final int TYPE_MEDIA = 4;
+
+  private OggPayload payloads[] = {
+    new TheoraDec(),
+    new VorbisDec(),
+    new KateDec()
+  };
+
+  private class OggStream extends Pad {
+    public int serialno;
+    public StreamState os;
+    private Vector headers;
+    private long baseTs;
+    public boolean haveHeaders;
+    public Vector queue;
+    public boolean started;
+    public boolean complete;
+    public boolean discont;
+    public boolean active;
+    public boolean haveKeyframe;
+    public boolean sentHeaders;
+    public int type;
+    public int lastRet;
+    
+    private OggPayload payload;
+
+	private OggStream (int serial) {
+      super (Pad.SRC, "serial_"+serial);
+      serialno = serial;
+      os = new StreamState();
+      os.init(serial);
+      os.reset();
+      queue = new Vector();
+      headers = new Vector();
+      haveHeaders = false;
+      haveKeyframe = false;
+      payload = null;
+      discont = true;
+      complete = false;
+      started = false;
+      baseTs = -1;
+      lastRet = OK;
+    }
+
+	private void markDiscont () {
+      discont = true;
+      complete = false;
+      haveKeyframe = false;
+      started = false;
+    }
+	private  void reset () {
+      markDiscont();
+      os.reset();
+      lastRet = OK;
+    }
+
+	private  boolean isComplete () {
+      return complete;
+    }
+	private  void activate() {
+      if (active)
+        return;
+
+      sentHeaders = false;
+      lastRet = OK;
+      addPad(this);
+      active = true;
+    }
+	private  void deActivate() {
+      if (!active)
+        return;
+      removePad(this);
+      pushEvent (Event.newEOS());
+      active = false;
+    }
+	private void reStart(long firstTs) {
+      com.fluendo.jst.Buffer buf;
+      long time;
+
+      if (!active)
+        return;
+
+      if (baseTs == -1)
+	baseTs = firstTs;
+
+      time = firstTs - baseTs;
+
+      Debug.log(Debug.DEBUG, this+" pushing segment start "+firstTs+", time "+time);
+      pushEvent (Event.newNewsegment (false, Format.TIME, firstTs, -1, time));
+
+      if (!sentHeaders) {
+        for (int i=0; i<headers.size(); i++) {
+          buf = (com.fluendo.jst.Buffer) headers.elementAt(i);
+          buf.setFlag (com.fluendo.jst.Buffer.FLAG_DISCONT, discont);
+	  discont = false;
+	  push (buf);
+        }
+	sentHeaders = true;
+      }
+      for (int i=0; i<queue.size(); i++) {
+        buf = (com.fluendo.jst.Buffer) queue.elementAt(i);
+	if (i == 0)
+          Debug.log(Debug.DEBUG, this+" first data buffer: "+buf.timestamp);
+        buf.setFlag (com.fluendo.jst.Buffer.FLAG_DISCONT, discont);
+	discont = false;
+	push (buf);
+      }
+      queue.setSize(0);
+      started = true;
+    }
+
+	private long getFirstTs () {
+      return payload.getFirstTs (queue);
+    }
+
+    private com.fluendo.jst.Buffer bufferFromPacket (Packet op)
+    {
+      com.fluendo.jst.Buffer data = com.fluendo.jst.Buffer.create();
+      data.copyData(op.packetBase, op.packet, op.bytes);
+      data.time_offset = op.granulepos;
+      if (payload != null)
+        data.timestamp = payload.granuleToTime (op.granulepos);
+      else
+        data.timestamp = -1;
+      data.setFlag (com.fluendo.jst.Buffer.FLAG_DISCONT, discont);
+      data.setFlag (com.fluendo.jst.Buffer.FLAG_DELTA_UNIT, !payload.isKeyFrame(op));
+
+      return data;
+    }
+
+
+    /* initialize a stream based on the first packet */
+    private void initNewStream (Packet op) {
+      int i;
+		
+      payload = null;
+      /* find out if it is a media payload */
+      for (i=0; i<payloads.length; i++) {
+	OggPayload pl = payloads[i];
+
+	if (pl.isType (op)) {
+		try {
+	    payload = (OggPayload) pl.getClass().newInstance();
+            /* we have a valid media type */
+            type = TYPE_MEDIA;
+            /* set mime type */
+            String mime = payload.getMime();
+            Debug.log(Debug.INFO, "new stream "+serialno+", mime "+mime);
+            setCaps (new Caps (mime));
+	    return;
+	  }
+	  catch (Exception e) {}
+	}
+      }
+      /* no payload, check for skeleton */
+      if (MemUtils.startsWith (op.packetBase, op.packet, op.bytes, fishead_signature)) {
+        type = TYPE_SKELETON;
+        Debug.log(Debug.INFO, "ignoring skeleton stream "+serialno);
+        postMessage (Message.newWarning (this, "ignoring skeleton stream "+serialno));
+	return;
+      }
+      /* check for cmml */
+      if (MemUtils.startsWith (op.packetBase, op.packet, op.bytes, cmml_signature)) {
+        type = TYPE_CMML;
+        Debug.log(Debug.INFO, "ignoring CMML stream "+serialno);
+        postMessage (Message.newWarning (this, "ignoring CMML stream "+serialno));
+	return;
+      }
+      /* else we don't know what it is */
+      type = TYPE_UNKNOWN;
+      Debug.log(Debug.INFO, "ignoring unknown stream "+serialno);
+      postMessage (Message.newWarning (this, "ignoring unknown stream "+serialno));
+    }
+
+	  private int pushPacket (Packet op) {
+      /* new stream, find out what it is  */
+      if (type == TYPE_NEW) {
+	initNewStream (op);
+      }
+      /* drop everything that is not recognized as media from here on. */
+      if (type != TYPE_MEDIA) {
+	complete = true;
+	return Pad.OK;
+      }
+
+      /* first read all the headers */
+      if (!haveHeaders) {
+		  if (payload.isHeader(op)) {
+			  int result = payload.takeHeader (op);
+          if (result < 0) {
+            postMessage (Message.newError (this, "cannot read header"));
+	    return Pad.ERROR;
+	  }
+          com.fluendo.jst.Buffer data = bufferFromPacket (op);
+          headers.addElement(data);
+          if (result > 0) {
+            haveHeaders = true;
+          }
+        }
+        else {
+          haveHeaders = true;
+        }
+      }
+      /* if we have all the headers we can stream */
+      if (haveHeaders) {
+        /* discontinuous codecs do not need to wait for data to allow playback */
+        if (!complete && payload.isDiscontinuous()) {
+          complete = true;
+        }
+        if (complete && started) {
+          int ret;
+          com.fluendo.jst.Buffer data = bufferFromPacket (op);
+	  ret = push (data);
+	  return combineFlows (this, ret);
+	}
+        if (haveKeyframe || payload.isKeyFrame(op)) {
+          com.fluendo.jst.Buffer data = bufferFromPacket (op);
+	  queue.addElement (data);
+	  haveKeyframe = true;
+	  if (op.granulepos != -1) {
+	    complete = true;
+	  }
+	}
+      }
+      return Pad.OK;
+    }
+
+	private int pushPage (Page og) {
+      int res;
+      int flowRet = Pad.OK;
+
+      res = os.pagein(og);
+      if (res < 0) {
+        // error; stream version mismatch perhaps
+        System.err.println("Error reading first page of Ogg bitstream data.");
+        postMessage (Message.newError (this, "Error reading first page of Ogg bitstream data."));
+        return ERROR;
+      }
+      while (flowRet == OK) {
+	res = os.packetout(op);
+        if(res == 0)
+	  break; // need more data
+        if(res == -1) {
+	  // missing or corrupt data at this page position
+          // no reason to complain; already complained above
+	  Debug.log(Debug.WARNING, "ogg error: packetout gave "+res);
+	  discont = true;
+        }
+        else {
+	  flowRet = pushPacket(op);
+        }
+      }
+      return flowRet;
+    }
+	protected  boolean eventFunc (com.fluendo.jst.Event event) {
+      return sinkPad.pushEvent (event);
+    }
+  }
+
+  private class OggChain {
+    private Vector streams;
+    private boolean active;
+    private boolean synced;
+    private long firstTs;
+
+	  private  OggChain () {
+      streams = new Vector();
+      synced = false;
+      active = false;
+      firstTs = -1;
+    }
+
+	private  boolean isActive() {
+      return active;
+    }
+
+	private  void activate() {
+      if (active)
+        return;
+
+      Debug.log(Debug.DEBUG, "activating chain");
+      for (int i=0; i<streams.size(); i++) {
+        OggStream stream = (OggStream) streams.elementAt(i);
+	stream.activate();
+      }
+      active = true;
+      noMorePads();
+    }
+	private void deActivate() {
+      if (!active)
+        return;
+      Debug.log(Debug.DEBUG, "deActivating chain");
+      for (int i=0; i<streams.size(); i++) {
+        OggStream stream = (OggStream) streams.elementAt(i);
+	stream.deActivate();
+      }
+      active = false;
+    }
+	private void reStart() {
+      if (!active)
+        return;
+
+      if (firstTs == -1) {
+        long maxTs = 0;
+        long minTs = Long.MAX_VALUE;
+        /* collect first timestamp */
+        for (int i=0; i<streams.size(); i++) {
+          OggStream stream = (OggStream) streams.elementAt(i);
+
+	  /* skip all streams not recognized as media streams */
+	  if (stream.type != TYPE_MEDIA)
+	    continue;
+
+	  long ts = stream.getFirstTs();
+	  maxTs = Math.max (maxTs, ts);
+	  minTs = Math.min (minTs, ts);
+        }
+	firstTs = maxTs;
+      }
+      for (int i=0; i<streams.size(); i++) {
+        OggStream stream = (OggStream) streams.elementAt(i);
+	stream.reStart(firstTs);
+      }
+    }
+
+	private void addStream (OggStream stream) {
+      streams.addElement (stream);
+    }
+
+	private  void markDiscont () {
+      synced = false;
+      firstTs = -1;
+      for (int i=0; i<streams.size(); i++) {
+	OggStream stream = (OggStream) streams.elementAt(i);
+	stream.markDiscont();
+      }
+    }
+
+	private OggStream findStream (int serial) {
+      OggStream stream = null;
+      for (int i=0; i<streams.size(); i++) {
+        stream = (OggStream) streams.elementAt(i);
+        if (stream.serialno == serial)
+          break;
+        stream = null;
+      }
+      return stream;
+    }
+	private  void resetStreams ()
+    {
+      for (int i=0; i<streams.size(); i++) {
+	OggStream stream = (OggStream) streams.elementAt(i);
+	stream.reset();
+      }
+    }
+	private boolean forwardEvent (com.fluendo.jst.Event event)
+    {
+      for (int i=0; i<streams.size(); i++) {
+	OggStream stream = (OggStream) streams.elementAt(i);
+	stream.pushEvent (event);
+      }
+      return true;
+    }
+
+	private int pushPage (Page og, OggStream stream) {
+      int flowRet = Pad.OK;
+	flowRet = stream.pushPage (og);
+
+      /* now check if all streams are Synced */
+      if (!synced) {
+        boolean check = true;
+	boolean hasMedia = false;
+        for (int i=0; i<streams.size(); i++) {
+	  OggStream cstream = (OggStream) streams.elementAt(i);
+
+	  if (cstream.type == TYPE_MEDIA) {
+	    hasMedia = true;
+	    if (!(check = cstream.isComplete()))
+	      break;
+          }
+        }
+	/* isComplete check do not work for annodex file right now, cause we don't parse
+	 * the annodex headers properly at this moment. So we shouldn't consider all
+	 * streams are synced unless we have at least one media stream which in turn
+	 * will ensure that all media streams are in sync. */
+	if (check && hasMedia) {
+          Debug.log(Debug.DEBUG, "streams synced");
+	  activate();
+	  reStart();
+	  synced = true;
+	}
+      }
+      return flowRet;
+    }
+  }
+
+  private Pad sinkPad = new Pad(Pad.SINK, "sink") {
+    protected boolean eventFunc (com.fluendo.jst.Event event)
+    {
+      switch (event.getType()) {
+        case Event.FLUSH_START:
+          if (chain != null)
+	    chain.forwardEvent (event);
+	  synchronized (streamLock) {
+            Debug.log(Debug.DEBUG, this+" synced");
+	  }
+	  break;
+        case Event.FLUSH_STOP:
+	  oy.reset();
+          if (chain != null) {
+	    chain.resetStreams();
+	    chain.forwardEvent (event);
+	  }
+	  break;
+        case Event.NEWSEGMENT:
+	  break;
+        case Event.EOS:
+	  Debug.log(Debug.INFO, "ogg: got EOS");
+          if (chain != null)
+	    chain.forwardEvent (event);
+	  else
+            postMessage (Message.newError (this, "unsupported media type"));
+	  break;
+        default:
+          if (chain != null)
+	    chain.forwardEvent (event);
+	  break;
+      }
+      return true;
+    }
+   protected synchronized int chainFunc (com.fluendo.jst.Buffer buf)
+    {
+      int res;
+      int flowRet = OK;
+
+      int index = oy.buffer(buf.length);
+
+      if (buf.isFlagSet (com.fluendo.jst.Buffer.FLAG_DISCONT)) {
+	Debug.log(Debug.INFO, "ogg: got discont");
+	if (chain != null) {
+	  chain.markDiscont ();
+	}
+      }
+		
+      System.arraycopy(buf.data, buf.offset, oy.data, index, buf.length);
+      oy.wrote(buf.length);
+  
+      // Loop over all pages in the buffer
+      while (flowRet == OK) {
+        res = oy.pageout(og);
+        if (res == 0)
+	  break; // need more data
+        if(res == -1) {
+	  // missing or corrupt data at this page position
+          // no reason to complain; already complained above
+	  Debug.log(Debug.WARNING, "ogg: pageout gave "+res);
+	  if (chain != null) {
+	    chain.markDiscont ();
+	  }
+        }
+        else {
+	  // Find the stream associated with this page
+	  int serial = og.serialno();
+	  OggStream stream = null;
+	  if (chain != null) {
+	    stream = chain.findStream (serial);
+	  }
+	  if (stream == null) {
+	    // No stream for this serial
+	    // The Ogg spec says that the bos pages for all the streams in a chain
+	    // will come before the remaining stream data for any stream. The chain
+	    // is activated once the non-header pages start arriving. So if a new
+	    // serial comes in when the chain is active, that means it must be the
+	    // start of a new chain.
+	    if (chain != null) {
+	      if (chain.isActive()) {
+	        chain.deActivate();
+	        chain = null;
+	      }
+	    }
+            if (chain == null)
+	      chain = new OggChain();
+
+	    // Create the new stream
+	    stream = new OggStream(serial);
+	    chain.addStream (stream);
+	  }
+	  flowRet = chain.pushPage (og, stream);
+        }
+      }
+      return flowRet;
+    }
+    protected boolean activateFunc (int mode)
+    {
+      if (mode == MODE_NONE) {
+	synchronized (sinkPad) {
+	  oy.reset();
+	  if (chain != null) {
+	    chain.deActivate();
+	    chain = null;
+	  }
+	}
+      }
+      return true;
+    }
+  };
+
+  private int combineFlows (OggStream stream, int ret) {
+    /* store the value */
+    stream.lastRet = ret;
+
+    /* if it's success we can return the value right away */
+    if (Pad.isFlowSuccess (ret))
+      return ret;
+
+    /* any other error that is not-linked can be returned right
+     * away */
+    if (ret != Pad.NOT_LINKED)
+      return ret;
+
+    /* only return NOT_LINKED if all other pads returned NOT_LINKED */
+    if (chain != null) {
+      for (int i=0; i<chain.streams.size(); i++) {
+	OggStream ostream = (OggStream) chain.streams.elementAt(i);
+
+        ret = ostream.lastRet;
+        /* some other return value (must be SUCCESS but we can return
+         * other values as well) */
+        if (ret != Pad.NOT_LINKED)
+          return ret;
+      }
+      /* if we get here, all other pads were unlinked and we return
+       * NOT_LINKED then */
+    }
+    return ret;
+  }
+
+  public String getFactoryName ()
+  {
+    return "oggdemux";
+  }
+  public String getMime ()
+  {
+    return "application/ogg";
+  }
+  public int typeFind (byte[] data, int offset, int length)
+  {
+    if (MemUtils.startsWith (data, offset, length, signature))
+      return 10;
+
+    return -1;
+  }
+
+  public OggDemux () {
+    super ();
+    oy = new SyncState();
+    og = new Page();
+    op = new Packet();
+
+    chain = null;
+
+    addPad (sinkPad);
+  }
+}

Added: trunk/cronus/com/fluendo/plugin/OggPayload.java
===================================================================
--- trunk/cronus/com/fluendo/plugin/OggPayload.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/plugin/OggPayload.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,62 @@
+/* Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.plugin;
+
+import java.util.*;
+import org.xiph.ogg.*;
+
+interface OggPayload
+{
+  /**
+   * Check if the packet contains the signature
+   * of the payload.
+   */
+  public boolean isType (Packet op);
+  /**
+   * Initialize the payload with a header packet.
+   * Returns < 0 for error, 0 if OK, 1 if OK and ready for decoding data.
+   */
+  public int takeHeader (Packet op);
+  /**
+   * Check if the packet contains a header packet
+   */
+  public boolean isHeader (Packet op);
+  /**
+   * Check if the packet contains a keyframe
+   */
+  public boolean isKeyFrame (Packet op);
+  /**
+   * Get the first timestamp of the list of packets
+   */
+  public long getFirstTs (Vector packets);
+  /**
+   * Convert the granule pos to a timestamp
+   */
+  public long granuleToTime (long gp);
+  /**
+   * Get mime type
+   */
+  public String getMime ();
+  /**
+   * Check if the stream is discontinuous (eg, no need to wait
+   * for data on this stream before playing)
+   */
+  public boolean isDiscontinuous ();
+}
+

Added: trunk/cronus/com/fluendo/plugin/Overlay.java
===================================================================
--- trunk/cronus/com/fluendo/plugin/Overlay.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/plugin/Overlay.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,122 @@
+/* Copyright (C) <2008> ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.plugin;
+
+import java.awt.*;
+import com.fluendo.jst.*;
+import com.fluendo.utils.*;
+
+/**
+ * This is a base overlay element, just passes images from sink to source.
+ * Extend this and override the overlay function to draw something onto
+ * images as they go from sink to source.
+ */
+public class Overlay extends Element
+{
+  protected Component component;
+
+  private Pad videoSrcPad = new Pad(Pad.SRC, "videosrc") {
+    protected boolean eventFunc (com.fluendo.jst.Event event) {
+      return videoSinkPad.pushEvent(event);
+    }
+  };
+
+  private Pad videoSinkPad = new Pad(Pad.SINK, "videosink") {
+    protected boolean eventFunc (com.fluendo.jst.Event event) {
+      return videoSrcPad.pushEvent (event);
+    }
+
+    /**
+     * Receives an image, allows a derived class to overlay whatever it wants on it,
+     * and sends it to the video source pad.
+     */
+    protected int chainFunc (com.fluendo.jst.Buffer buf) {
+      int result;
+
+      Debug.log( Debug.DEBUG, parent.getName() + " <<< " + buf );
+
+      overlay(buf);
+
+      result = videoSrcPad.push(buf);
+      if (result != Pad.OK) {
+        Debug.log( Debug.WARNING, parent.getName() + ": failed to push buffer to video source pad: "+result);
+      }
+
+      return result;
+    }
+
+    protected boolean activateFunc (int mode)
+    {
+      return true;
+    }
+  };
+
+  public Overlay() {
+    super();
+
+    addPad (videoSinkPad);
+    addPad (videoSrcPad);
+  }
+
+  /**
+   * this function may be overridden to draw whatever the derived
+   * class wants onto the incoming image.
+   * By default, the image is passed without alteration.
+   */
+  protected void overlay(com.fluendo.jst.Buffer buf) {
+    /* straight pass through by default */
+  }
+
+  public boolean setProperty (String name, java.lang.Object value) {
+    if (name.equals("component")) {
+      component = (Component) value;
+    }
+    else {
+      return super.setProperty(name, value);
+    }
+
+    return true;
+  }
+
+  public java.lang.Object getProperty (String name) {
+    if (name.equals("component")) {
+      return component;
+    }
+    else {
+      return super.getProperty(name);
+    }
+  }
+
+  /* from the video sink code, I do not understand what this does semantically,
+     the frame would be 0x0 sized. Maybe just to avoid possible null dereference,
+     but I suspect there might be something more clever, so it goes in for safety */
+  protected int changeState (int transition) {
+    if (currentState == STOP && pendingState == PAUSE && component == null) {
+      Frame frame = new Frame();
+      component = (Component) frame;
+    }
+    return super.changeState(transition);
+  }
+
+  public String getFactoryName ()
+  {
+    return "overlay";
+  }
+}
+

Added: trunk/cronus/com/fluendo/plugin/Queue.java
===================================================================
--- trunk/cronus/com/fluendo/plugin/Queue.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/plugin/Queue.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,400 @@
+/* Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is close software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.plugin;
+
+import java.util.*;
+import com.fluendo.jst.*;
+import com.fluendo.utils.*;
+
+public class Queue extends Element
+{
+  /* leaky types */
+  public static final int NO_LEAK = 0;
+  public static final int LEAK_UPSTREAM = 1;
+  public static final int LEAK_DOWNSTREAM = 2;
+
+  private static final int DEFAULT_MAX_BUFFERS = 100;
+  private static final int DEFAULT_MAX_SIZE = -1;
+  private static final boolean DEFAULT_IS_BUFFER = false;
+  private static final int DEFAULT_LOW_PERCENT = 10;
+  private static final int DEFAULT_HIGH_PERCENT = 70;
+  private static final int DEFAULT_LEAKY = NO_LEAK;
+  
+  private Vector queue = new Vector();
+  private int srcResult = Pad.WRONG_STATE;
+  private int size;
+  private boolean isBuffering;
+  private boolean isEOS;
+  private boolean headNeedsDiscont = false;
+  private boolean tailNeedsDiscont = false;
+
+  private int maxBuffers = DEFAULT_MAX_BUFFERS;
+  private int maxSize = DEFAULT_MAX_SIZE;
+  private boolean isBuffer = DEFAULT_IS_BUFFER;
+  private int lowPercent = DEFAULT_LOW_PERCENT;
+  private int highPercent = DEFAULT_HIGH_PERCENT;
+  private int leaky = DEFAULT_LEAKY;
+
+  private boolean isFilled() {
+    if (maxSize != -1) {
+      return size >= maxSize;
+    }
+    else {
+      return queue.size() >= maxBuffers;
+    }
+  }
+  private boolean isEmpty() {
+    return queue.size() == 0;
+  }
+
+  private void clearQueue ()
+  {
+    for (Enumeration e = queue.elements(); e.hasMoreElements();) {
+      java.lang.Object obj = e.nextElement();
+      if (obj instanceof Buffer)
+        ((Buffer)obj).close();
+    }
+    queue.setSize(0);
+    size = 0;
+    isBuffering = true;
+  }
+
+  private void updateBuffering () {
+    if (!isBuffer || srcResult != Pad.OK)
+      return;
+    if (isEOS) {
+      if (isBuffering) {
+	isBuffering = false;
+	postMessage(Message.newBuffering(this, false, 0));
+      }
+      return;
+    }
+
+    /* figure out the percentage we are filled */
+    int percent = size * 100 / maxSize;
+    if (percent > 100)
+      percent = 100;
+
+    if (isBuffering) {
+      if (percent >= highPercent) {
+        isBuffering = false;
+      }
+      postMessage (Message.newBuffering (this, isBuffering, percent));
+    }
+    else {
+      if (percent < lowPercent) {
+        isBuffering = true;
+      }
+    }
+  }
+  
+  private void leakDownstream () {
+    /* for as long as the queue is filled, dequeue an item and discard it */
+    java.lang.Object leak;
+    while (isFilled()) {
+      synchronized (queue) {
+        leak = queue.lastElement();
+        if (leak == null) {
+          Debug.error("There is nothing to dequeue and the queue is still filled. This should not happen.");
+        }
+        queue.removeElementAt(queue.size()-1);
+        if (leak instanceof Buffer)
+          ((Buffer)leak).close();
+        headNeedsDiscont = true;
+        queue.notifyAll();
+      }
+    }
+  }
+
+  private Pad srcpad = new Pad(Pad.SRC, "src") {
+    protected void taskFunc() {
+      java.lang.Object obj;
+      int res;
+      
+      synchronized (queue) {
+        if (srcResult != OK)
+	  return;
+	  
+	while (isEmpty()) {
+          try {
+            queue.wait();
+	    if (srcResult != OK)
+	      return;
+	  }
+	  catch (InterruptedException ie) {}
+	}
+	obj = queue.elementAt(queue.size()-1);
+	queue.removeElement(obj);
+        queue.notifyAll();
+      }
+
+      if (obj instanceof Event) {
+        Event event = (Event) obj;
+        pushEvent(event);
+	res = OK;
+	if (event.getType() == Event.EOS) {
+	  postMessage (Message.newStreamStatus (this, false, OK, "flow stopped, EOS"));
+	  pauseTask();
+	}
+      }
+      else {
+        Buffer buf = (Buffer) obj;
+        
+        if (headNeedsDiscont) {
+          buf.setFlag(Buffer.FLAG_DISCONT, true);
+          headNeedsDiscont = false;
+        }
+
+	size -= buf.length;
+
+	Debug.log( Debug.DEBUG, parent.getName() + " >>> " + buf );
+        res = push(buf);
+	if ( maxSize == -1 ) {
+	  Debug.log( Debug.DEBUG, parent.getName() + " count = " + queue.size() + "/" + maxBuffers );
+	} else {
+	  Debug.log( Debug.DEBUG, parent.getName() + " size = " + size + "/" + maxSize );
+	}
+      }
+      synchronized (queue) {
+        if (res != OK) {
+	  srcResult = res;
+	  if (isFlowFatal (res)) {
+            pushEvent(Event.newEOS());
+	  }
+	  postMessage (Message.newStreamStatus (this, false, res, "flow stopped"));
+	  pauseTask();
+        }
+	updateBuffering ();
+      }
+    }
+
+    protected boolean activateFunc (int mode)
+    {
+      boolean res = true;
+
+      switch (mode) {
+        case MODE_NONE:
+	  synchronized (queue) {
+	    clearQueue();
+	    srcResult = WRONG_STATE;
+	    queue.notifyAll();
+	  }
+	  // Cancel buffering status
+	  if (isBuffer && isBuffering) {
+	    isBuffering = false;
+	    postMessage(Message.newBuffering(this, false, 0));
+	  }
+	  postMessage (Message.newStreamStatus (this, false, Pad.WRONG_STATE, "stopping"));
+          res = stopTask();
+          break;
+        case MODE_PUSH:
+	  isEOS = false;
+	  synchronized (queue) {
+	    srcResult = OK;
+	    /* if we buffer, we start when we are hitting the
+	     * high watermark */
+	    if (!isBuffer) {
+	      isBuffering = false;
+	    }
+	    else {
+	      isBuffering = true;
+	      postMessage (Message.newBuffering (this, true, 0));
+	    }
+	    postMessage (Message.newStreamStatus (this, true, Pad.OK, "activating"));
+            res = startTask("cortado-Queue-Stream-"+Debug.genId());
+	  }
+          break;
+        default:
+	  synchronized (queue) {
+	    srcResult = WRONG_STATE;
+	  }
+          res = false;
+          break;
+      }
+      return res;
+    }
+  };
+	  
+
+  private Pad sinkpad = new Pad(Pad.SINK, "sink") {
+    protected boolean eventFunc (Event event) {
+      int type = event.getType();
+      boolean doQueue = true;
+
+      switch (type) {
+        case Event.FLUSH_START:
+	   srcpad.pushEvent (event);
+	   synchronized (queue) {
+	     srcResult = WRONG_STATE;
+	     queue.notifyAll();
+	   }
+	   synchronized (streamLock) {
+	     Debug.log(Debug.DEBUG, this+" synced");
+	   }
+	   postMessage (Message.newStreamStatus (srcpad, false, Pad.WRONG_STATE, "flush start"));
+	   srcpad.pauseTask();
+	   doQueue = false;
+	   break;
+        case Event.FLUSH_STOP:
+	   srcpad.pushEvent (event);
+
+	   isEOS = false;
+	   synchronized (queue) {
+	     clearQueue ();
+	     srcResult = OK;
+	     queue.notifyAll();
+	   }
+	   if (isBuffer) {
+	     isBuffering = true;
+	     postMessage (Message.newBuffering (this, true, 0));
+	   }
+	   postMessage (Message.newStreamStatus (srcpad, true, Pad.OK, "restart after flush"));
+           srcpad.startTask("cortado-Queue-Stream-"+Debug.genId());
+	   doQueue = false;
+	   break;
+        case Event.EOS:
+	   isEOS = true;
+	   Debug.log(Debug.INFO, "got EOS: "+this);
+	   if (isBuffer) {
+	     if (isBuffering) {
+	       isBuffering = false;
+               postMessage (Message.newBuffering (this, isBuffering, 100));
+	     }
+	   }
+	   break;
+        case Event.NEWSEGMENT:
+        default:
+	   break;
+      }
+      if (doQueue) {
+        synchronized (queue) {
+          queue.insertElementAt(event, 0);
+          queue.notifyAll();
+        }
+      }
+      return true;
+    }
+
+    protected int chainFunc (Buffer buf) {
+      synchronized (queue) {
+        if (srcResult != OK) {
+	  buf.close();
+	  return srcResult;
+	}
+
+	while (isFilled()) {
+	  switch (leaky) {
+	    case LEAK_UPSTREAM:
+	      tailNeedsDiscont = true;
+	      Debug.debug(parent.getName() + "is full, leaking buffer on upstream end");
+	      buf.close();
+	      queue.notifyAll();
+	      return OK;
+	    case LEAK_DOWNSTREAM:
+	      leakDownstream();
+	      break;
+	    default:
+	      Debug.warn("Unknown leaky type, using default");
+	      /* fall-through */
+	    case NO_LEAK:
+              try {
+	        Debug.debug(parent.getName() + " full, waiting...");
+                queue.wait();
+	        if (srcResult != OK) {
+	          buf.close();
+	          return srcResult;
+	        }
+	      }
+	      catch (InterruptedException ie) {
+	        ie.printStackTrace();
+	        buf.close();
+	        return WRONG_STATE;
+	      }
+	      break;
+	  }
+	}
+	
+	if (tailNeedsDiscont) {
+	  buf.setFlag(Buffer.FLAG_DISCONT, true);
+	  tailNeedsDiscont = false;
+	}
+	
+	size += buf.length;
+	updateBuffering();
+
+	Debug.log( Debug.DEBUG, parent.getName() + " <<< " + buf );
+        queue.insertElementAt(buf, 0);
+	if ( maxSize == -1 ) {
+	  Debug.log( Debug.DEBUG, parent.getName() + " count = " + queue.size() + "/" + maxBuffers );
+	} else {
+	  Debug.log( Debug.DEBUG, parent.getName() + " size = " + size + "/" + maxSize );
+	}
+        queue.notifyAll();
+      }
+      return OK;
+    }
+  };
+
+  public Queue() {
+    super();
+    addPad (srcpad);
+    addPad (sinkpad);
+  }
+
+  public String getFactoryName() {
+    return "queue";
+  }
+
+  public boolean setProperty (String name, java.lang.Object value) {
+    if (name.equals("maxBuffers"))
+      maxBuffers = Integer.valueOf(value.toString()).intValue();
+    else if (name.equals("maxSize"))
+      maxSize = Integer.valueOf(value.toString()).intValue();
+    else if (name.equals("isBuffer"))
+      isBuffer = String.valueOf(value).equalsIgnoreCase("true");
+    else if (name.equals("lowPercent"))
+      lowPercent = Integer.valueOf(value.toString()).intValue();
+    else if (name.equals("highPercent"))
+      highPercent = Integer.valueOf(value.toString()).intValue();
+    else if (name.equals("leaky"))
+      leaky = Integer.valueOf(value.toString()).intValue();
+    else
+      return false;
+
+    return true;
+  }
+
+  public java.lang.Object getProperty (String name) {
+    if (name.equals("maxBuffers"))
+      return new Integer (maxBuffers);
+    else if (name.equals("maxSize"))
+      return new Integer (maxSize);
+    else if (name.equals("isBuffer"))
+      return (isBuffer ? "true" : "false");
+    else if (name.equals("lowPercent"))
+      return new Integer (lowPercent);
+    else if (name.equals("highPercent"))
+      return new Integer (highPercent);
+    else if (name.equals("leaky"))
+      return new Integer (leaky);
+
+    return null;
+  }
+}
+

Added: trunk/cronus/com/fluendo/plugin/Selector.java
===================================================================
--- trunk/cronus/com/fluendo/plugin/Selector.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/plugin/Selector.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,137 @@
+/* Copyright (C) <2008> ogg.k.ogg.k <ogg.k.ogg.k at googlemail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.plugin;
+
+import java.util.*;
+import com.fluendo.jst.*;
+import com.fluendo.utils.*;
+
+/**
+ * This element receives data from N sinks, and selects one of them
+ * to send from its source.
+ */
+public class Selector extends Element
+{
+  private Vector sinks = new Vector();
+  int selected = -1;
+  Pad selectedPad = null;
+
+  private Pad srcPad = new Pad(Pad.SRC, "src") {
+    /**
+     * Pushes the event to every sink.
+     */
+    protected boolean eventFunc (com.fluendo.jst.Event event) {
+      boolean ret = true;
+      for (int n=0; n<sinks.size(); ++n) {
+        ret &= ((Pad)sinks.elementAt(n)).pushEvent(event);
+      }
+      return ret;
+    }
+  };
+
+  private int findPad (Pad pad) {
+    for (int n=0; n<sinks.size(); ++n) {
+      if (sinks.elementAt(n) == pad)
+        return n;
+    }
+    return -1;
+  }
+
+  /**
+   * Requests a new sink pad to be created for the given peer.
+   * The caps do not matter, as Selector is a caps agnostic element.
+   */
+  public Pad requestSinkPad(Pad peer) {
+    Pad pad = new Pad(Pad.SINK, "sink"+sinks.size()) {
+      protected boolean eventFunc (com.fluendo.jst.Event event) {
+        if (selectedPad == this) {
+          return srcPad.pushEvent (event);
+        }
+        return true;
+      }
+
+      protected int chainFunc (com.fluendo.jst.Buffer buf) {
+        int result = Pad.OK;
+
+        //Debug.log( Debug.DEBUG, parent.getName() + " <<< " + buf );
+        Debug.debug("Selector got "+buf.caps+" buffer on "+this.toString());
+
+        if (selectedPad == this) {
+          Debug.debug("what a coincidence, we're selected - pushing");
+          result = srcPad.push(buf);
+        }
+
+        return result;
+      }
+
+      protected boolean activateFunc (int mode)
+      {
+        return true;
+      }
+    };
+
+    sinks.addElement(pad);
+    addPad(pad);
+    return pad;
+  }
+
+  public Selector() {
+    super();
+
+    addPad (srcPad);
+  }
+
+  /**
+   * The selected sink may be selected via the "selected" property - negative to select nothing
+   */
+  public boolean setProperty (String name, java.lang.Object value) {
+    if (name.equals("selected")) {
+      int new_selected = Integer.valueOf(value.toString()).intValue();
+      Debug.info("Selector: request to select "+new_selected+" (from "+selected+"), within 0-"+(sinks.size()-1));
+      if (new_selected < 0 || new_selected >= sinks.size()) {
+        selected = -1;
+        selectedPad = null;
+      }
+      else {
+        selected = new_selected;
+        selectedPad = (Pad)sinks.elementAt(selected);
+      }
+    }
+    else {
+      return super.setProperty(name, value);
+    }
+
+    return true;
+  }
+
+  public java.lang.Object getProperty (String name) {
+    if (name.equals("selected")) {
+      return new Integer(selected);
+    }
+    else {
+      return super.getProperty(name);
+    }
+  }
+
+  public String getFactoryName ()
+  {
+    return "selector";
+  }
+}
+

Added: trunk/cronus/com/fluendo/plugin/SmokeDec.java
===================================================================
--- trunk/cronus/com/fluendo/plugin/SmokeDec.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/plugin/SmokeDec.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,137 @@
+/* Smoke Codec
+ * Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.plugin;
+
+import java.awt.*;
+import com.fluendo.codecs.*;
+import com.fluendo.jst.*;
+import com.fluendo.utils.*;
+
+public class SmokeDec extends Element
+{
+  private Component component;
+  private MediaTracker mediaTracker;
+  private SmokeCodec smoke;
+  private int width, height;
+
+  private Pad srcPad = new Pad(Pad.SRC, "src") {
+    protected boolean eventFunc (com.fluendo.jst.Event event) {
+      return sinkPad.pushEvent(event);
+    }
+  };
+
+  private Pad sinkPad = new Pad(Pad.SINK, "sink") {
+    protected boolean eventFunc (com.fluendo.jst.Event event) {
+      boolean result;
+
+      switch (event.getType()) {
+        case com.fluendo.jst.Event.FLUSH_START:
+          result = srcPad.pushEvent (event);
+          synchronized (streamLock) {
+            Debug.log(Debug.INFO, "synced "+this);
+          }
+          break;
+        case com.fluendo.jst.Event.FLUSH_STOP:
+          result = srcPad.pushEvent(event);
+          break;
+        case com.fluendo.jst.Event.EOS:
+        case com.fluendo.jst.Event.NEWSEGMENT:
+        default:
+          result = srcPad.pushEvent(event);
+          break;
+      }
+      return result;
+    }
+    protected int chainFunc (com.fluendo.jst.Buffer buf) {
+      int ret;
+      Image img = null;
+
+      img = smoke.decode(buf.data, buf.offset, buf.length);
+      if (img != null) {
+        if (img.getWidth(null) != width || img.getHeight(null) != height) {
+          width = img.getWidth(null);
+          height = img.getHeight(null);
+
+          Debug.log(Debug.INFO, "smoke frame: "+width+","+height);
+
+          caps = new Caps ("video/raw");
+          caps.setFieldInt ("width", width);
+          caps.setFieldInt ("height", height);
+          caps.setFieldInt ("aspect_x", 1);
+          caps.setFieldInt ("aspect_y", 1);
+        }
+        buf.object = img;
+        buf.caps = caps;
+
+        ret = srcPad.push(buf);
+      }
+      else {
+        if ((smoke.flags & SmokeCodec.KEYFRAME) != 0) {
+          Debug.log (Debug.WARNING, "could not decode jpeg image");
+	}
+        buf.close();
+        ret = OK;
+      }
+      return ret;
+    }
+  };
+
+  public SmokeDec() {
+    super();
+
+    addPad(srcPad);
+    addPad(sinkPad);
+  }
+
+  public boolean setProperty (String name, java.lang.Object value) {
+    if (name.equals("component")) {
+      component = (Component) value;
+      mediaTracker = new MediaTracker (component);
+      smoke = new SmokeCodec (component, mediaTracker);
+    }
+    else
+      return false;
+
+    return true;
+  }
+
+  public java.lang.Object getProperty (String name) {
+    if (name.equals("component")) {
+      return component;
+    }
+    return null;
+  }
+
+  public String getFactoryName ()
+  {
+    return "smokedec";
+  }
+  public String getMime ()
+  {
+    return "video/x-smoke";
+  }
+  public int typeFind (byte[] data, int offset, int length)
+  {
+    if (data[offset+1] == 0x73) {
+      return 10;
+    }
+    return -1;
+  }
+}

Added: trunk/cronus/com/fluendo/plugin/TextOverlay.java
===================================================================
--- trunk/cronus/com/fluendo/plugin/TextOverlay.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/plugin/TextOverlay.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,109 @@
+/* Copyright (C) <2008> ogg.k.ogg.k <ogg.k.ogg.k at googlecode.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.plugin;
+
+import java.awt.*;
+import java.awt.image.*;
+
+/**
+ * This class displays a simple text string on top of incoming video.
+ */
+public class TextOverlay extends Overlay
+{
+  private int font_size = -1;
+  private Font font = null;
+  private String text = null;
+
+  public TextOverlay() {
+    super();
+  }
+
+  /**
+   * Display a text string (from a property) onto the image.
+   */
+  protected void overlay(com.fluendo.jst.Buffer buf) {
+    Image img;
+
+    /* img retrieval from VideoSink.java */
+    if (buf.object instanceof ImageProducer) {
+      img = component.createImage((ImageProducer)buf.object);
+    }
+    else if (buf.object instanceof Image) {
+      img = (Image)buf.object;
+    }
+    else {
+      System.out.println(this+": unknown buffer received "+buf);
+      return;
+    }
+
+    Dimension d = component.getSize();
+    int x = 0;
+    int y = 0;
+    int w = d.width;
+    int h = d.height;
+
+    int new_font_size = w / 32;
+    if (new_font_size < 12) new_font_size = 12;
+    if (font == null || new_font_size != font_size) {
+      font_size = new_font_size;
+      font = new Font("sans", Font.BOLD, font_size); // TODO: should be selectable ?
+    }
+
+    Graphics g = img.getGraphics();
+    g.drawImage(img, x, y, w, h, null);
+
+    /* render text on top */
+    if (text != null) {
+      double tw;
+      g.setFont(font);
+      g.setColor(Color.white);
+      FontMetrics fm = g.getFontMetrics();
+      tw = fm.stringWidth(text);
+      g.drawString(text, x+(int)((w-tw)/2), y+(int)(h*0.85));
+    }
+
+    g.dispose();
+  }
+
+  public boolean setProperty (String name, java.lang.Object value) {
+    if (name.equals("text")) {
+      text = value.toString();
+    }
+    else {
+      return super.setProperty(name, value);
+    }
+
+    return true;
+  }
+
+  public java.lang.Object getProperty (String name) {
+    if (name.equals("text")) {
+      return text;
+    }
+    else {
+      return super.getProperty(name);
+    }
+  }
+
+  public String getFactoryName ()
+  {
+    return "textoverlay";
+  }
+}
+

Added: trunk/cronus/com/fluendo/plugin/TheoraDec.java
===================================================================
--- trunk/cronus/com/fluendo/plugin/TheoraDec.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/plugin/TheoraDec.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,303 @@
+/* Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.plugin;
+
+import java.util.*;
+import org.xiph.ogg.*;
+import com.meviatronic.zeus.pollux.*;
+import com.fluendo.jst.*;
+import com.fluendo.utils.*;
+
+/**
+ * @author Fluendo, Michael Scheerer (Adaptions, Fixes)
+ */
+
+public class TheoraDec extends Element implements OggPayload
+{
+  private static final byte[] signature = { -128, 0x74, 0x68, 0x65, 0x6f, 0x72, 0x61 };
+
+  private VideoReader ti;
+  private static TheoraDecoder td;
+  private static Packet op;
+  private int packet;
+
+  private long lastTs;
+  private boolean needKeyframe;
+  private boolean haveDecoder;
+  private static boolean haveComment;
+	
+  /*
+   * OggPayload interface
+   */
+  public boolean isType (Packet op)
+  {
+    return typeFind (op.packetBase, op.packet, op.bytes) > 0;
+  }
+  public int takeHeader (Packet op)
+	{
+		byte header;
+		
+		int ret = 0;
+		
+		try {
+			ti.readMediaInformation(op);
+		} catch (Exception e) {
+			ret = -1;
+		}
+		
+		header = op.packetBase[op.packet];
+		
+	if (header == -126 && !haveDecoder) {
+		td = ti.initializeTheoraDecoder();
+		if (!haveComment) {
+			System.out.println(ti.getOggCommentContent());
+			haveComment = true;
+		}
+		haveDecoder = true;
+    }
+    return ret;
+  }
+  public boolean isHeader (Packet op)
+  {
+    return (op.packetBase[op.packet] & 0x80) == 0x80;
+  }
+  public boolean isKeyFrame (Packet op)
+  {
+	  return (op.packetBase[op.packet] & 0x40) == 0;
+  }
+  public boolean isDiscontinuous ()
+  {
+    return false;
+  }
+  public long getFirstTs (Vector packets)
+  {
+    int len = packets.size();
+    int i;
+    long time;
+    com.fluendo.jst.Buffer data = null;
+
+    /* first find buffer with valid offset */
+    for (i=0; i<len; i++) {
+      data = (com.fluendo.jst.Buffer) packets.elementAt(i);
+
+      if (data.time_offset != -1)
+        break;
+    }
+    if (i == packets.size())
+      return -1;
+
+    time = granuleToTime (data.time_offset);
+
+    data = (com.fluendo.jst.Buffer) packets.elementAt(0);
+    data.timestamp = time - (long) ((i+1) * (Clock.SECOND / ti.frameRate));
+
+    return time;
+  }
+  public long granuleToTime (long gp)
+  {
+    long res;
+
+    if (gp < 0 || !haveDecoder)
+      return -1;
+	res = (long) (td.granuleTime(gp) * Clock.SECOND);
+
+    return res;
+  }
+
+  private Pad srcPad = new Pad(Pad.SRC, "src") {
+    protected boolean eventFunc (com.fluendo.jst.Event event) {
+      return sinkPad.pushEvent(event);
+    }
+  };
+
+  private Pad sinkPad = new Pad(Pad.SINK, "sink") {
+    protected boolean eventFunc (com.fluendo.jst.Event event) {
+      boolean result;
+
+      switch (event.getType()) {
+        case com.fluendo.jst.Event.FLUSH_START:
+	  result = srcPad.pushEvent (event);
+	  synchronized (streamLock) {
+            Debug.log(Debug.DEBUG, "synced "+this);
+	  }
+          break;
+        case com.fluendo.jst.Event.FLUSH_STOP:
+          result = srcPad.pushEvent(event);
+          break;
+        case com.fluendo.jst.Event.EOS:
+          Debug.log(Debug.INFO, "got EOS "+this);
+          result = srcPad.pushEvent(event);
+          break;
+        case com.fluendo.jst.Event.NEWSEGMENT:
+	default:
+          result = srcPad.pushEvent(event);
+          break;
+      }
+      return result;
+    }
+
+    protected int chainFunc (com.fluendo.jst.Buffer buf) {
+    	int result;
+      	long timestamp;
+
+      	Debug.log( Debug.DEBUG, parent.getName() + " <<< " + buf );
+
+      	op.packetBase = buf.data;
+      	op.packet = buf.offset;
+      	op.bytes = buf.length;
+      	op.bos = (packet == 0 ? 1 : 0);
+      	op.eos = 0;
+      	op.packetno = packet;
+      	timestamp = buf.timestamp;
+
+      	if (buf.isFlagSet (com.fluendo.jst.Buffer.FLAG_DISCONT)) {
+        	Debug.log(Debug.INFO, "theora: got discont");
+        	needKeyframe = true;
+			lastTs = -1;
+	  	}
+		
+		if (packet < 3) {
+		 	if (takeHeader(op) < 0) {
+          		buf.close();
+          		// error case; not a theora header
+          		Debug.log(Debug.ERROR, "does not contain Theora video data.");
+          		return ERROR;
+        	}
+		  	if (packet == 2) {
+           		Debug.log(Debug.INFO, "theora dimension: "+ti.codedPictureWidth+"x"+ti.codedPictureHeight);
+          		Debug.log(Debug.INFO, "theora offset: "+ti.pictureRegionX+","+ti.pictureRegionY);
+          		Debug.log(Debug.INFO, "theora frame: "+ti.pictureRegionW+","+ti.pictureRegionH);
+          		Debug.log(Debug.INFO, "theora aspect: "+ti.aspectRatio.width+"/"+ti.aspectRatio.height);
+          		Debug.log(Debug.INFO, "theora framerate: "+ti.frameRate);
+
+	  			caps = new Caps ("video/raw");
+	  			caps.setFieldInt ("width", ti.pictureRegionW);
+	  			caps.setFieldInt ("height", ti.pictureRegionH);
+	  			caps.setFieldInt ("aspect_x", ti.aspectRatio.width);
+	  			caps.setFieldInt ("aspect_y", ti.aspectRatio.height);
+        	}
+       	 	buf.close();
+        	packet++;
+			return OK;
+		} else {
+			if (op.bytes == 0) {
+				Debug.log(Debug.DEBUG, "duplicate frame");
+				return OK;
+			}
+        	if ((op.packetBase[op.packet] & 0x80) == 0x80) {
+          		Debug.log(Debug.INFO, "ignoring header");
+          		return OK;
+			}
+			if (needKeyframe && isKeyFrame(op)) {
+	  			needKeyframe = false;
+        	}
+
+			if (timestamp != -1) {
+	  			lastTs = timestamp;
+			} else if (lastTs != -1) {
+	  			long add;
+
+	  			add = (long) (Clock.SECOND / ti.frameRate);
+	  			lastTs += add;
+	  			timestamp = lastTs;
+			}
+
+			if (!needKeyframe) {
+				try{
+					buf.object = td.synthesis(op);
+          		} catch (Exception e) { // Dirty hack!!!!!!!!
+					e.printStackTrace();
+					e.getMessage().equals(" ");
+	    			postMessage (Message.newError (this, e.getMessage()));
+            		result = ERROR;
+				}
+				buf.caps = caps;
+	    		buf.timestamp = timestamp;
+            	Debug.log( Debug.DEBUG, parent.getName() + " >>> " + buf );
+            	result = srcPad.push(buf);
+			} else {
+				result = OK;
+	  			buf.close();
+			}
+	 	}
+		packet++;
+
+      	return result;
+    }
+
+	protected boolean activateFunc (int mode) {
+    	return true;
+    }
+  };
+
+	public TheoraDec() {
+    	super();
+	  	if (ti == null) {
+		  	ti = new VideoReader();
+	  	}
+	 
+	  	if (op == null) {
+		  	op = new Packet();
+	  	}
+	  
+    	addPad (srcPad);
+	  	addPad (sinkPad);
+  	}
+
+  protected int changeState (int transition) {
+    int res;
+
+    switch (transition) {
+      case STOP_PAUSE:
+        lastTs = -1;
+        packet = 0;
+        needKeyframe = true;
+	break;
+      default:
+        break;
+    }
+
+    res = super.changeState (transition);
+
+    switch (transition) {
+      case PAUSE_STOP:
+	
+	break;
+      default:
+        break;
+    }
+
+    return res;
+  }
+
+  public String getFactoryName ()
+	{
+    return "theoradec";
+  }
+  public String getMime ()
+  {
+    return "video/x-theora";
+  }
+  public int typeFind (byte[] data, int offset, int length)
+  {
+    if (MemUtils.startsWith (data, offset, length, signature))
+      return 10;
+    return -1;
+  }
+}

Added: trunk/cronus/com/fluendo/plugin/VideoSink.java
===================================================================
--- trunk/cronus/com/fluendo/plugin/VideoSink.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/plugin/VideoSink.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,208 @@
+/* Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.plugin;
+
+import java.awt.*;
+import java.awt.image.*;
+import com.fluendo.utils.*;
+import com.fluendo.jst.*;
+
+public class VideoSink extends Sink
+{
+  private Component component;
+  private boolean keepAspect;
+  private boolean ignoreAspect;
+  private boolean scale;
+  private Frame frame;
+
+  private int width, height;
+  private int aspectX, aspectY;
+  private Rectangle bounds;
+
+  public VideoSink ()
+  {
+    keepAspect = true;
+    scale = true;
+    bounds = null;
+  }
+
+  protected boolean setCapsFunc (Caps caps)
+  {
+    String mime = caps.getMime();
+    if (!mime.equals ("video/raw"))
+      return false;
+
+    width = caps.getFieldInt("width", -1);
+    height = caps.getFieldInt("height", -1);
+
+    if (width == -1 || height == -1)
+      return false;
+
+    aspectX = caps.getFieldInt("aspect_x", 1);
+    aspectY = caps.getFieldInt("aspect_y", 1);
+
+    if(!ignoreAspect) {
+      Debug.log(Debug.DEBUG, this+" dimension: "+width+"x"+height+", aspect: "+aspectX+"/"+aspectY);
+
+      if (aspectY > aspectX) {
+        height = height * aspectY / aspectX;
+      } else {
+        width = width * aspectX / aspectY;
+      }
+      Debug.log(Debug.DEBUG, this+" scaled source: "+width+"x"+height);
+    }
+
+    component.setVisible(true);
+
+    return true;
+  }
+
+  protected int preroll (Buffer buf)
+  {
+    return render (buf);
+  }
+
+  protected int render (Buffer buf)
+  {
+    Image image;
+    int x, y, w, h;
+
+    Debug.log( Debug.DEBUG, this.getName() + " starting buffer " + buf );
+
+    if (buf.object instanceof ImageProducer) {
+      image = component.createImage((ImageProducer)buf.object);
+    }
+    else if (buf.object instanceof Image) {
+      image = (Image)buf.object;
+    }
+    else {
+      System.out.println(this+": unknown buffer received "+buf);
+      return Pad.ERROR;
+    }
+
+    if (!component.isVisible())
+      return Pad.NOT_NEGOTIATED;
+
+    Graphics graphics = component.getGraphics();
+
+    if (keepAspect) {
+      double src_ratio, dst_ratio;
+
+      if (bounds == null) {
+	bounds = new Rectangle(component.getSize());
+      }
+      src_ratio = (double) width / height;
+      dst_ratio = (double) bounds.width / bounds.height;
+
+      if (src_ratio > dst_ratio) {
+        w = bounds.width;
+        h = (int) (bounds.width / src_ratio);
+        x = bounds.x;
+        y = bounds.y + (bounds.height - h) / 2;
+      } else if (src_ratio < dst_ratio) {
+        w = (int) (bounds.height * src_ratio);
+        h = bounds.height;
+        x = bounds.x + (bounds.width - w) / 2;
+        y = bounds.y;
+      } else {
+        x = bounds.x;
+        y = bounds.y;
+        w = bounds.width;
+        h = bounds.height;
+      }
+    } else if (!scale) {
+      w = Math.min (width, bounds.width);
+      h = Math.min (height, bounds.height);
+      x = bounds.x + (bounds.width - w) / 2;
+      y = bounds.y + (bounds.height - h) / 2;
+    } else {
+      /* draw in available area */
+      w = bounds.width;
+      h = bounds.height;
+      x = 0;
+      y = 0;
+    }
+    graphics.drawImage (image, x, y, w, h, null);
+    Debug.log( Debug.DEBUG, this.getName() + " done with buffer " + buf );
+
+    return Pad.OK;
+  };
+
+  public String getFactoryName ()
+  {
+    return "videosink";
+  }
+
+  /*
+   * component:    A java.awt.Component where the video frames will be sent
+   * keep-aspect:  String, if "true", the aspect ratio of the input video will be maintained
+   * scale:        String, if "true", and keep-aspect is not true, the video
+   *               will be scaled to fit the bounding rectangle
+   *
+   * bounds:       A java.awt.Rectangle giving the output bounding rectangle.
+   *               This must always be set after component, because setting
+   *               component resets the bounding rectangle to the full extent
+   *               of the component.
+   */
+  public boolean setProperty (String name, java.lang.Object value) {
+    if (name.equals("component")) {
+      component = (Component) value;
+    }
+    else if (name.equals("keep-aspect")) {
+      keepAspect = String.valueOf(value).equals("true");
+    } else if(name.equals("ignore-aspect")) {
+      ignoreAspect = value.toString().equals("true");
+    } else if (name.equals("scale")) {
+      scale = String.valueOf(value).equals("true");
+    } else if (name.equals("bounds")) {
+      bounds = (Rectangle) value;
+      Debug.info("Video bounding rectangle: x=" +
+	bounds.x + ", y=" +
+	bounds.y + ", w=" +
+	bounds.width + ", h=" +
+	bounds.height );
+    }
+    else {
+      return super.setProperty(name, value);
+    }
+
+    return true;
+  }
+
+  public java.lang.Object getProperty (String name) {
+    if (name.equals("component")) {
+      return component;
+    }
+    else if (name.equals("keep-aspect")) {
+      return (keepAspect ? "true": "false");
+    } else if (name.equals("bounds")) {
+      return bounds;
+    } else {
+      return super.getProperty(name);
+    }
+  }
+
+  protected int changeState (int transition) {
+    if (currentState == STOP && pendingState == PAUSE && component == null) {
+      frame = new Frame();
+      component = (Component) frame;
+    }
+    return super.changeState(transition);
+  }
+}

Added: trunk/cronus/com/fluendo/plugin/VorbisDec.java
===================================================================
--- trunk/cronus/com/fluendo/plugin/VorbisDec.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/plugin/VorbisDec.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,298 @@
+/* Copyright (C) <2004> Wim Taymans <wim at fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.plugin;
+
+import java.util.*;
+import org.xiph.ogg.*;
+import com.meviatronic.zeus.castor.*;
+import com.fluendo.jst.*;
+import com.fluendo.utils.*;
+
+/**
+ * @author Fluendo, Michael Scheerer (Adaptions, Fixes)
+ */
+
+public class VorbisDec extends Element implements OggPayload
+{
+  	private long packet;
+  	private long offset;
+  	private AudioReader vi;
+  	private static VorbisDecoder vd;
+  	private boolean discont;
+	private static Packet op;
+	static byte[] convbuffer;
+  	
+  private static final byte[] signature = { 0x01, 0x76, 0x6f, 0x72, 0x62, 0x69, 0x73 };
+
+  public boolean isType (Packet op)
+  {
+    return typeFind (op.packetBase, op.packet, op.bytes) > 0;
+  }
+  public int takeHeader (Packet op) {
+		byte header;
+	  
+		int ret = 0;
+		
+		try {
+			vi.readMediaInformation(op);
+		} catch (Exception e) {
+			ret = -1;
+		}
+    	return ret;
+  }
+  public boolean isHeader (Packet op)
+  {
+    return (op.packetBase[op.packet] & 0x01) == 0x01;
+  }
+  public boolean isKeyFrame (Packet op)
+  {
+    return true;
+  }
+  public boolean isDiscontinuous ()
+  {
+    return false;
+  }
+  public long getFirstTs (Vector packets)
+  {
+    int len = packets.size();
+    int i;
+    long total = 0;
+    long prevSamples = 0;
+    Packet p = new Packet();
+
+    com.fluendo.jst.Buffer buf;
+
+    /* add samples */
+    for (i=0; i<len; i++) {
+      boolean ignore;
+      long temp;
+
+      buf = (com.fluendo.jst.Buffer) packets.elementAt(i);
+
+      p.packetBase = buf.data;
+      p.packet = buf.offset;
+      p.bytes = buf.length;
+
+      long samples = vi.blocksize(p);
+      if (samples <= 0)
+        return -1;
+
+      if (prevSamples == 0 ) {
+        prevSamples = samples;
+        /* ignore first packet */
+        ignore = true;
+      }
+      else
+        ignore = false;
+
+      temp = (samples + prevSamples) / 4;
+      prevSamples = samples;
+
+      if (!ignore)
+        total += temp;
+ 
+      if (buf.time_offset != -1) {
+        total = buf.time_offset - total;
+	long result = granuleToTime (total);
+
+        buf = (com.fluendo.jst.Buffer) packets.elementAt(0);
+	buf.timestamp = result;
+        return result;
+      }
+    }
+    return -1;
+  }
+  public long granuleToTime (long gp)
+  {
+    if (gp < 0)
+      return -1;
+	 
+    return gp * Clock.SECOND / vi.sampleRate;
+  }
+
+  private Pad srcPad = new Pad(Pad.SRC, "src") {
+    protected boolean eventFunc (com.fluendo.jst.Event event) {
+      return sinkPad.pushEvent(event);
+    }
+  };
+
+  private Pad sinkPad = new Pad(Pad.SINK, "sink") {
+    protected boolean eventFunc (com.fluendo.jst.Event event) {
+      boolean result;
+
+      switch (event.getType()) {
+        case Event.FLUSH_START:
+          result = srcPad.pushEvent(event);
+	  synchronized (streamLock) {
+	    Debug.log(Debug.DEBUG, "synced "+this);
+	  }
+	  break;
+        case Event.FLUSH_STOP:
+          result = srcPad.pushEvent(event);
+	  break;
+        case Event.EOS:
+          Debug.log(Debug.INFO, "got EOS "+this);
+          result = srcPad.pushEvent(event);
+	  break;
+        default:
+          result = srcPad.pushEvent(event);
+	  break;
+      }
+      return result;
+    }
+	  
+	protected int chainFunc (com.fluendo.jst.Buffer buf) {
+    	int result = OK;
+      	long timestamp;
+
+      	op.packetBase = buf.data;
+      	op.packet = buf.offset;
+      	op.bytes = buf.length;
+      	op.bos = (packet == 0 ? 1 : 0);
+      	op.eos = 0;
+      	op.packetno = packet;
+
+      	if (buf.isFlagSet (com.fluendo.jst.Buffer.FLAG_DISCONT)) {
+			offset = -1;
+			discont = true;
+        	Debug.log(Debug.INFO, "vorbis: got discont");
+      	}
+
+		if (packet < 3) {
+			try {
+				vi.readMediaInformation(op);
+			} catch (Exception e) {
+				// error case; not a vorbis header
+	  			Debug.log(Debug.ERROR, "This Ogg bitstream does not contain Vorbis audio data.");
+	  			return ERROR;
+			}
+        	
+        	if (packet == 2) {
+	  			Debug.log(Debug.INFO, "vorbis rate: "+vi.sampleRate);
+				Debug.log(Debug.INFO, "vorbis channels: "+vi.channels);
+				
+	  			caps = new Caps ("audio/raw");
+	  			caps.setFieldInt ("width", 16);
+	  			caps.setFieldInt ("depth", 16);
+	  			caps.setFieldInt ("rate", vi.sampleRate);
+	  			caps.setFieldInt ("channels", vi.channels);
+        	}
+        	buf.close();
+        	packet++;
+
+			return OK;
+      } else {
+		  	try {
+			  	if (vd == null) {
+					vd = vi.initializeVorbisDecoder();
+					System.out.println(vi.getOggCommentContent());
+			  	}
+			} catch (Exception e) {}
+
+		  	if (isHeader(op)) {
+          		Debug.log(Debug.INFO, "ignoring header");
+	  			return OK;
+		  	}
+
+        	timestamp = buf.timestamp;
+		  
+		  	if (timestamp != -1) {
+	  			offset = timestamp * vi.sampleRate / Clock.SECOND;
+        	} else {
+          		timestamp = offset * Clock.SECOND / vi.sampleRate;
+			}
+	
+		  	int samples = vd.getNumberOfSamples() / vi.channels;
+		  
+		  	try {
+				convbuffer = vd.synthesis(op);
+			} catch (Exception e) {
+				Debug.log(Debug.ERROR, "decoding error");
+	  			return ERROR;
+			}
+
+		  	int numbytes = 2 * vd.getNumberOfSamples();
+		  
+	  		buf.ensureSize(numbytes);
+	  		buf.offset = 0;
+	  		buf.timestamp = timestamp;
+	  		buf.time_offset = offset;
+	  		buf.length = numbytes;
+	  		buf.caps = caps;
+	  		buf.setFlag (com.fluendo.jst.Buffer.FLAG_DISCONT, discont);
+	  		discont = false;
+			System.arraycopy(convbuffer, 2 * vd.getSampleOffset(), buf.data, 0, numbytes);
+		  	offset += samples;
+		   	if ((result = srcPad.push(buf)) != OK) {
+				return ERROR;
+			}
+	  	}
+    	packet++;
+
+    	return result;
+	}
+};
+
+  	public VorbisDec() {
+		super();
+	  
+	  	if (vi == null) {
+		  	vi = new AudioReader();
+	  	}
+	 
+	  	if (op == null) {
+		  	op = new Packet();
+	  	}
+
+    	addPad (srcPad);
+    	addPad (sinkPad);
+  	}
+
+  protected int changeState (int transition) {
+    int res;
+
+    switch (transition) {
+      case STOP_PAUSE:
+        packet = 0;
+		offset = -1;
+        break;
+      default:
+        break;
+    }
+
+    res = super.changeState (transition);
+
+    return res;
+  }
+
+  public String getFactoryName ()
+  {
+    return "vorbisdec";
+  }
+  public String getMime ()
+  {
+    return "audio/x-vorbis";
+  }
+  public int typeFind (byte[] data, int offset, int length)
+  {
+    if (MemUtils.startsWith (data, offset, length, signature))
+      return 10;
+    return -1;
+  }
+}

Added: trunk/cronus/com/fluendo/utils/Base64Converter.java
===================================================================
--- trunk/cronus/com/fluendo/utils/Base64Converter.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/utils/Base64Converter.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,90 @@
+/* Cortado - a video player java applet
+ * Copyright (C) 2004 Fluendo S.L.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.utils;
+
+public class Base64Converter
+{
+  public static final char[] alphabet = {
+    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',	//  0 to  7
+    'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',	//  8 to 15
+    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',	// 16 to 23
+    'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',	// 24 to 31
+    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',	// 32 to 39
+    'o', 'p', 'q', 'r', 's', 't', 'u', 'v',	// 40 to 47
+    'w', 'x', 'y', 'z', '0', '1', '2', '3',	// 48 to 55
+    '4', '5', '6', '7', '8', '9', '+', '/'
+  };				// 56 to 63
+
+
+  public static String encode (byte[]octetString)
+  {
+    int bits24;
+    int bits6;
+
+    char[] out = new char[((octetString.length - 1) / 3 + 1) * 4];
+
+    int outIndex = 0;
+    int i = 0;
+
+    while ((i + 3) <= octetString.length)
+      {
+	// store the octets
+	bits24 = (octetString[i++] & 0xFF) << 16;
+	bits24 |= (octetString[i++] & 0xFF) << 8;
+	bits24 |= (octetString[i++] & 0xFF) << 0;
+
+	bits6 = (bits24 & 0x00FC0000) >> 18;
+	out[outIndex++] = alphabet[bits6];
+	bits6 = (bits24 & 0x0003F000) >> 12;
+	out[outIndex++] = alphabet[bits6];
+	bits6 = (bits24 & 0x00000FC0) >> 6;
+	out[outIndex++] = alphabet[bits6];
+	bits6 = (bits24 & 0x0000003F);
+	out[outIndex++] = alphabet[bits6];
+      }
+
+    if (octetString.length - i == 2)
+      {
+	bits24 = (octetString[i] & 0xFF) << 16;
+	bits24 |= (octetString[i + 1] & 0xFF) << 8;
+
+	bits6 = (bits24 & 0x00FC0000) >> 18;
+	out[outIndex++] = alphabet[bits6];
+	bits6 = (bits24 & 0x0003F000) >> 12;
+	out[outIndex++] = alphabet[bits6];
+	bits6 = (bits24 & 0x00000FC0) >> 6;
+	out[outIndex++] = alphabet[bits6];
+
+	out[outIndex++] = '=';
+      }
+    else if (octetString.length - i == 1)
+      {
+	bits24 = (octetString[i] & 0xFF) << 16;
+
+	bits6 = (bits24 & 0x00FC0000) >> 18;
+	out[outIndex++] = alphabet[bits6];
+	bits6 = (bits24 & 0x0003F000) >> 12;
+	out[outIndex++] = alphabet[bits6];
+
+	out[outIndex++] = '=';
+	out[outIndex++] = '=';
+      }
+    return new String (out);
+  }
+}

Added: trunk/cronus/com/fluendo/utils/Debug.java
===================================================================
--- trunk/cronus/com/fluendo/utils/Debug.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/utils/Debug.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,83 @@
+/* Cortado - a video player java applet
+ * Copyright (C) 2004 Fluendo S.L.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.utils;
+
+public class Debug {
+  public static final int NONE = 0;
+  public static final int ERROR = 1;
+  public static final int WARNING = 2;
+  public static final int INFO = 3;
+  public static final int DEBUG = 4;
+
+  public static int level = INFO;
+
+  /* static id counter */
+  private static int counter = 0;
+  private static long startTime = 0;
+
+  public static final int genId() {
+    synchronized (Debug.class) {
+      return counter++;
+    }
+  }
+
+  public static final String[] prefix = {
+       "NONE",
+       "ERRO",
+       "WARN",
+       "INFO",
+       "DBUG"};
+
+  public static String rpad(String s, int length) {
+    if ( length > s.length() ) {
+      int sz = length - s.length();
+      char arr[] = new char[sz];
+      for (int n=0; n<sz; ++n)
+        arr[n] = ' ';
+      return s + new String( arr );
+    } else {
+      return s;
+    }
+  }
+
+  public static void log(int lev, String line)
+  {
+    long t = System.currentTimeMillis();
+    if ( startTime == 0 ) {
+      startTime = t;
+    }
+    t -= startTime;
+    
+    if (lev <= level) {
+      if (level >= DEBUG) {
+        System.out.println( "[" + Debug.rpad( Thread.currentThread().getName(), 30 ) + " "
+	  + Debug.rpad( Long.toString( t ), 6 ) + " " + prefix[lev] + "] " + line );
+      } else {
+	System.out.println( "[" + prefix[lev] + "] " + line );
+      }
+    }
+  }
+
+  public static void error(String line) { Debug.log( ERROR, line ); }
+  public static void warning(String line) { Debug.log( WARNING, line ); }
+  public static void warn(String line) { Debug.log( WARNING, line ); }
+  public static void info(String line) { Debug.log( INFO, line ); }
+  public static void debug(String line) { Debug.log( DEBUG, line ); }
+}
+

Added: trunk/cronus/com/fluendo/utils/MemUtils.java
===================================================================
--- trunk/cronus/com/fluendo/utils/MemUtils.java	                        (rev 0)
+++ trunk/cronus/com/fluendo/utils/MemUtils.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,116 @@
+/* Cortado - a video player java applet
+ * Copyright (C) 2004 Fluendo S.L.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+package com.fluendo.utils;
+
+public class MemUtils {
+  private static final char[] bytes = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };
+
+  public static final int cmp (byte[] mem1, byte[] mem2, int len)
+  {
+    for (int i=0; i<len; i++) {
+      if (mem1[i] != mem2[i]) {
+        if (mem1[i] < mem2[i]) 
+	  return -i;
+	else
+	  return i;
+      }
+    }
+    return 0;
+  }
+  public static final void set (byte[] mem, int offset, int val, int len)
+  {
+    len += offset;
+
+    for (int i=offset; i<len; i++) {
+      mem[i] = (byte)val;
+    }
+  }
+  public static final void set (short[] mem, int offset, int val, int len)
+  {
+    len += offset;
+
+    for (int i=offset; i<len; i++) {
+      mem[i] = (short)val;
+    }
+  }
+  public static final void set (int[] mem, int offset, int val, int len)
+  {
+    len += offset;
+
+    for (int i=offset; i<len; i++) {
+      mem[i] = (int)val;
+    }
+  }
+  public static final void set (Object[] mem, int offset, Object val, int len)
+  {
+    len += offset;
+
+    for (int i=offset; i<len; i++) {
+      mem[i] = val;
+    }
+  }
+  /* check if a given arr starts with the given pattern */
+  public static final boolean startsWith (byte[] arr, int offset, int len, byte[] pattern)
+  {
+    int length = pattern.length;
+    int i;
+
+    if (len < length)
+      return false;
+
+    for (i=0; i < length; i++)
+      if (arr[offset+i] != pattern[i])
+        break;
+
+    return i == length;
+  }
+
+  public static final void dump (byte[] mem, int start, int len)
+  {
+    int i, j;
+    StringBuffer string = new StringBuffer(50);
+    StringBuffer chars = new StringBuffer(18);
+    String vis = new String (mem, start, len);
+
+    i = j = 0;
+    while (i < len) {
+      int b = ((int)mem[i+start]); if (b<0) b+=256;
+
+      if (b > 0x20 && b < 0x7f)
+	chars.append (vis.charAt(i));
+      else
+	chars.append (".");
+
+      string.append (bytes[b/16]);
+      string.append (bytes[b%16]);
+      string.append (" ");
+
+      j++;
+      i++;
+
+      if (j == 16 || i == len) {
+	System.out.println ("" + (i-j) + "  "+ string.toString() + chars.toString());
+
+        string.setLength(0);
+        chars.setLength(0);
+        j = 0;
+      }
+    }
+  }
+}

Added: trunk/cronus/com/meviatronic/zeus/castor/AudioReader.java
===================================================================
--- trunk/cronus/com/meviatronic/zeus/castor/AudioReader.java	                        (rev 0)
+++ trunk/cronus/com/meviatronic/zeus/castor/AudioReader.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,1078 @@
+/* Castor, a fast Vorbis decoder created by Michael Scheerer.
+ *
+ * Castor decoder (c) 2010 Michael Scheerer www.meviatronic.com
+ *
+ * Many thanks to
+ *   Monty <monty at xiph.org> and
+ *   The XIPHOPHORUS Company http://www.xiph.org/ .
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+   
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+package com.meviatronic.zeus.castor;
+
+import com.meviatronic.zeus.helen.*;
+import org.xiph.ogg.*;
+
+import java.io.*;
+
+/**
+ * The <code>AudioReader</code> class provides all necessary audio format detection related methods.
+ * The <code>AudioReader</code> class stors also audio header related data.
+ *
+ * @author	Michael Scheerer
+ */
+public class AudioReader {
+	
+	public final static int BITMASK[] = {
+		0x00000000,
+		0x00000001, 0x00000003, 0x00000007, 0x0000000F,
+		0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF,
+		0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF,
+		0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF,
+		0x0001FFFF, 0x0003FFFF, 0x0007FFFF, 0x000FFFFF,
+		0x001FFFFF, 0x003FFFFF, 0x007FFFFF, 0x00FFFFFF,
+		0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF,
+		0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF
+	};
+
+	protected final static int BYTELENGTH = 8;
+	protected final static int DOUBLE_BYTELENGTH = 16;
+	protected final static int TRIPLE_BYTELENGTH = 24;
+	protected final static int QUADRUPEL_BYTELENGTH = 32;
+
+	private final static int VORBIS_CODEBOOK_SYNC_PATTERN = 0x564342;
+	
+	private static VorbisDecoder dec;
+	private static Tag tag;
+	
+	public static int version;
+  	public static int channels;
+	public static int sampleRate;
+	public static int bitRateMax  ;
+  	public static int bitRate;
+	public static int bitRateMin;
+	
+	private static int byteIdx;
+	private static byte[] data;
+	private static int revBitIdx;
+	private static int packetByteIdx;
+	private static int packetSize;
+	
+	// Vorbis extendet header
+	private static HuffTreeEntry[] hts;
+	private static int[] codebookCodewordLengths;
+	
+	static int[] codebookDimensions;
+	static float[][] valueVector;
+	static int[] blocksizes = new int[2];
+	static int[] vorbisFloorTypes;
+	static int[] floor1Multiplieres;
+	static int[][] floor1PartitionClassList;
+	static int[][] floor1ClassDimensions;
+	static int[][] floor1ClassSubclasses;
+	static int[][] floor1ClassMasterbooks;
+	static int[][] floor1XList;
+	static int floor1MaximumValues;
+	static int[][][] floor1SubclassBooks;
+	static int[] vorbisResidueTypes;
+	static int[] residueMaximumPasses;
+	static int[] residueBegin;
+	static int[] residueEnd;
+	static int[] residuePartitionSize;
+	static int[] residueClassifications;
+	static int[] residueClassbooks;
+	static int[][] residueCascade;
+	static int[][][] residueBooks;
+	static int[][] vorbisMappingMagnitude;
+	static int[][] vorbisMappingAngle;
+	static int[][] vorbisMappingMux;
+	static int[][] vorbisMappingSubmapFloor;
+	static int[][] vorbisMappingSubmapResidue;
+	static int[] vorbisModeBlockflag;
+	static int[] vorbisModeWindowtype;
+	static int[] vorbisModeTransformtype;
+	static int[] vorbisModeMapping;
+	static int modeNumberBits;
+	
+	private static boolean vbr;
+	
+	private static boolean headerInitialized1;
+	private static boolean headerInitialized2;
+	private static boolean headerInitialized3;
+	
+	public void loadPacket(byte[] buf, int start, int bytes){
+		byteIdx = start;
+		data = buf;
+		revBitIdx = packetByteIdx = 0;
+		packetSize = bytes;
+	}
+	
+  	private void verifyFirstPacket() throws IOException, EndOfPacketException {
+		if (getLittleEndian(32) != 0) {
+			throw new InterruptedIOException("Vorbis version not 0");
+		}
+		channels = getLittleEndian(8);
+		sampleRate = getLittleEndian(32);
+		int bitRateMax = getLittleEndian(32);
+		bitRate = getLittleEndian(32);
+		int bitRateMin = getLittleEndian(32);
+			
+		vbr = true;
+		if (!(bitRateMax == bitRateMin && bitRateMax  == bitRate)) {
+			vbr = false;
+		}
+		blocksizes[0] = getLittleEndian(4);
+		blocksizes[1] = getLittleEndian(4);
+			
+		if (blocksizes[0] == 0 || blocksizes[1] == 0 || blocksizes[0] > 13 || blocksizes[1] > 13 || blocksizes[1] < blocksizes[0]) {
+			throw new InterruptedIOException("Wrong block size");
+		}
+		blocksizes[0] = 1 << blocksizes[0];
+		blocksizes[1] = 1 << blocksizes[1];
+		if (getLittleEndian1() == 0) {
+			throw new InterruptedIOException("Wrong framing bit");
+		}
+		if (channels == 0 || channels > 2) {
+			throw new InterruptedIOException("Wrong number of channels");
+		}
+		if (sampleRate > 48000 || sampleRate == 0) {
+			throw new InterruptedIOException("Wrong sample rate");
+		}
+		if (bitRate == 0) {
+			if (bitRateMax > 0 & bitRateMin > 0) {
+				bitRate = (bitRateMax + bitRateMin) / 2;
+			} else if (bitRateMax > 0) {
+				bitRate = bitRateMax;
+			} else if (bitRateMax > 0) {
+				bitRate = bitRateMin;
+			}
+		}
+		headerInitialized1 = true;
+  	}
+	
+	private final void verifySecondPacket() throws IOException, EndOfPacketException {
+		try {
+			tag = new OggTag(this, true);
+			((OggTag) tag).decode();
+		} catch (Exception e) {
+			if (e instanceof IOException) {
+				throw new InterruptedIOException(e.getMessage());
+			}
+		}
+		headerInitialized2 = true;
+	}
+	
+	private final void verifyThirdPacket() throws IOException, EndOfPacketException {
+		int i, j, k;
+		
+		// 4.2.4.1. Codebooks
+
+		int vorbisCodebookCount = getLittleEndian(8) + 1;
+		
+		codebookDimensions = new int[vorbisCodebookCount];
+		hts = new HuffTreeEntry[vorbisCodebookCount];
+		valueVector = new float[vorbisCodebookCount][];
+		float[] valueVectorPointer = null;
+		
+		for (i = 0; i < vorbisCodebookCount; i++) {
+
+			if (getLittleEndian(24) != VORBIS_CODEBOOK_SYNC_PATTERN) {
+				throw new InterruptedIOException("Broken codebook sync pattern");
+			}
+		
+			int codebookDimension = getLittleEndian(16);
+			codebookDimensions[i] = codebookDimension;
+			int codebookEntries = getLittleEndian(24);
+			
+			int usedEntries = 0;
+			int ordered = getLittleEndian1();
+			codebookCodewordLengths = new int[codebookEntries];
+			int currentLength = 0;
+			int maxLength = 0;
+			int currentEntry = 0;
+
+			if (ordered == 0) {
+				int sparse = getLittleEndian1();
+				int flag;
+			
+				for (j = 0; j < codebookEntries; j++) {
+					if(sparse == 1) {
+						flag = getLittleEndian1();
+						if(flag == 1) {
+							currentLength = codebookCodewordLengths[j] = getLittleEndian(5) + 1;
+						}
+					} else {
+						currentLength = codebookCodewordLengths[j] = getLittleEndian(5) + 1;
+					}
+					if (currentLength > maxLength) {
+						maxLength = currentLength;
+					}
+				}
+			} else {
+				currentLength = getLittleEndian(5) + 1;
+				int number;
+				int end;
+			
+				if (currentLength > maxLength) {
+					maxLength = currentLength;
+				}
+				do {
+					number = getLittleEndian(ilog(codebookEntries - currentEntry));
+					end = currentEntry + number;
+			
+					for (j = currentEntry; j < end; j++) {
+						codebookCodewordLengths[j] = currentLength;
+					}
+					currentEntry = number + currentEntry;
+					currentLength++;
+					if (currentEntry > codebookEntries) {
+						throw new InterruptedIOException("Codebook overflow");
+					}
+					if (currentLength > maxLength) {
+						maxLength = currentLength;
+					}
+				} while (currentEntry < codebookEntries);
+			}
+			
+			for (j = 0; j < codebookEntries; j++) {
+				if(codebookCodewordLengths[j] > 0) {
+					usedEntries++;
+					currentEntry = j;
+				}
+			}
+
+			HuffTreeEntry node = null;
+			
+			if (usedEntries == 1) {
+				node = new HuffTreeEntry();
+				
+				node.sparse = true;
+				node.value = currentEntry;
+			} else if (usedEntries > 1) {
+				node = new HuffTreeEntry();
+				
+				buildTree(node, maxLength);
+				deflateTree(node);
+				pruneTree(node);
+			}
+
+			hts[i] = node;
+			
+			int codebookLookupType = getLittleEndian(4);
+			
+			if (codebookLookupType > 2) {
+				throw new InterruptedIOException("Code lookup type overflow");
+			} else if (codebookLookupType == 0) {
+				// Lookup type zero indicates no lookup to be read. Proceed past lookup decode.
+				if (valueVectorPointer == null) {
+					valueVector[i] = new float[codebookDimension * codebookEntries];
+				} else {
+					valueVector[i] = valueVectorPointer;
+				}
+				continue;
+			}
+			
+			float codebookMinimumValue = float32Unpack(getLittleEndian(32));
+			float codebookDeltaValue = float32Unpack(getLittleEndian(32));
+			int codebookValueBits = getLittleEndian(4) + 1;
+			int codebookSequenceP = getLittleEndian1();
+			int codebookLookupValues = 0;
+			
+			if (codebookLookupType == 1) {
+				codebookLookupValues = (int) Math.floor(Math.pow(codebookEntries, 1F / codebookDimension));
+			} else {
+				codebookLookupValues = codebookEntries * codebookDimension;
+			}
+			
+			int[] codebookMultiplicands = new int[codebookLookupValues];
+			
+			for (j = 0; j < codebookLookupValues; j++) {
+				codebookMultiplicands[j] = getLittleEndian(codebookValueBits);
+			}
+			
+			if (usedEntries == 0) {
+				continue;
+			}
+
+			float last;
+			int indexDivisor;
+			int multiplicandOffset;
+			valueVectorPointer = new float[codebookDimension * codebookEntries];
+			float valueVectorContent = 0;
+			
+			if (codebookLookupType == 1) {
+				for (j = 0; j < codebookEntries; j++) {
+					last = 0;
+					indexDivisor = 1;
+				
+					for (k = 0; k < codebookDimension; k++) {
+						multiplicandOffset = (j / indexDivisor) % codebookLookupValues;
+						valueVectorContent = codebookMultiplicands[multiplicandOffset] * codebookDeltaValue + codebookMinimumValue + last;
+						if (codebookSequenceP == 1) {
+							last = valueVectorContent;
+						}
+						valueVectorPointer[j * codebookDimension + k] = valueVectorContent;
+						indexDivisor *= codebookLookupValues;
+					}
+				}
+			} else {
+				for (j = 0; j < codebookEntries; j++) {
+					last = 0;
+					multiplicandOffset = j * codebookDimension;
+				
+					for (k = 0; k < codebookDimension; k++) {
+						valueVectorContent = codebookMultiplicands[multiplicandOffset + k] * codebookDeltaValue + codebookMinimumValue + last;
+
+						if (codebookSequenceP == 1) {
+							last = valueVectorContent;
+						}
+						valueVectorPointer[multiplicandOffset + k] = valueVectorContent;
+					}
+				}
+			}
+			valueVector[i] = valueVectorPointer;
+		}
+		
+		codebookCodewordLengths = null;
+		
+		// 4.2.4.2. Time domain transforms
+
+		int vorbisTimeCount = getLittleEndian(6) + 1;
+		
+		for (i = 0; i < vorbisTimeCount; i++) {
+			if (getLittleEndian(16) != 0) {
+				throw new IOException("Unexcepted end of time domain setup");
+			}
+		}
+		
+		// 4.2.4.3. Floors
+		
+		int vorbisFloorCount = getLittleEndian(6) + 1;
+	
+		vorbisFloorTypes = new int[vorbisFloorCount];
+		int vorbisFloorTyp;
+		int floor1Partitions;
+		int maximumClass;
+		int floor1PartitionClass;
+		int floor1ClassSubclass;
+		int floor1SubclassRange;
+		int floor1Multiplier;
+		int rangebits;
+		int floor1Values;
+		int floor1ClassDimensionsElement;
+		floor1Multiplieres = new int[vorbisFloorCount];
+		floor1PartitionClassList = new int[vorbisFloorCount][];
+		floor1ClassDimensions = new int[vorbisFloorCount][];
+		floor1ClassSubclasses = new int[vorbisFloorCount][];
+		floor1ClassMasterbooks = new int[vorbisFloorCount][];
+		floor1SubclassBooks = new int[vorbisFloorCount][][];
+		floor1XList = new int[vorbisFloorCount][];
+		
+		for (i = 0; i < vorbisFloorCount; i++) {
+			vorbisFloorTyp = getLittleEndian(16);
+			if (vorbisFloorTyp == 0 || vorbisFloorTyp > 1) {
+				throw new IOException("No support of floor type zero or wrong floor type");
+			}
+			floor1Partitions = getLittleEndian(5);
+			floor1PartitionClassList[i] = new int[floor1Partitions];
+			maximumClass = -1;
+			
+			for (j = 0; j < floor1Partitions; j++) {
+				floor1PartitionClass = getLittleEndian(4);
+				if (floor1PartitionClass > maximumClass) {
+					maximumClass = floor1PartitionClass;
+				}
+				floor1PartitionClassList[i][j] = floor1PartitionClass;
+			}
+
+			int[] dimensions = floor1ClassDimensions[i] = new int[maximumClass + 1];
+			int[] subclasses = floor1ClassSubclasses[i] = new int[maximumClass + 1];
+			int[] masterbooks = floor1ClassMasterbooks[i] = new int[maximumClass + 1];
+			int[][] subclassbooks = floor1SubclassBooks[i] = new int[maximumClass + 1][];
+			
+			for (j = 0; j < maximumClass + 1; j++) {
+				dimensions[j] = getLittleEndian(3) + 1;
+				floor1ClassSubclass = getLittleEndian(2);
+				
+				if (floor1ClassSubclass != 0) {
+					if ((masterbooks[j] = getLittleEndian(8)) >= vorbisCodebookCount) {
+						throw new IOException("Number of classbooks can't be GE than number of huffman books");
+					}
+				}
+				subclasses[j] = floor1ClassSubclass;
+				
+				floor1SubclassRange = 1 << floor1ClassSubclass;
+				
+				subclassbooks[j] = new int[floor1SubclassRange];
+				
+				for (k = 0; k < floor1SubclassRange; k++) {
+					if ((subclassbooks[j][k] = getLittleEndian(8) - 1) >= vorbisCodebookCount) {
+						throw new IOException("Number of floor subclass books can't be GE than number of huffman books");
+					}
+				}
+			}
+
+			floor1Multiplier = getLittleEndian(2) + 1;
+			rangebits = getLittleEndian(4);
+			floor1Values = 2;
+			
+			int[] floor1ClassDimensionsElementTable = new int[floor1Partitions];
+			
+			for (j = 0; j < floor1Partitions; j++) {
+				floor1ClassDimensionsElementTable[j] = floor1ClassDimensionsElement = floor1ClassDimensions[i][floor1PartitionClassList[i][j]];
+				floor1Values += floor1ClassDimensionsElement;
+			}
+
+			floor1XList[i] = new int[floor1Values];
+			if (floor1Values > floor1MaximumValues) {
+				floor1MaximumValues = floor1Values;
+			}
+			floor1XList[i][0] = 0;
+			floor1XList[i][1] = 1 << rangebits;
+			floor1Values = 2;
+			
+			for (j = 0; j < floor1Partitions; j++) {
+				floor1ClassDimensionsElement = floor1ClassDimensionsElementTable[j];
+				
+				for (k = 0; k < floor1ClassDimensionsElement; k++) {
+					if ((floor1XList[i][floor1Values] = getLittleEndian(rangebits)) >= 1 << rangebits) {
+						throw new IOException("Floor book values can't be GE than 1 << rangebits");
+					}
+					floor1Values++;
+				}
+			}
+			vorbisFloorTypes[i] = vorbisFloorTyp;
+			floor1Multiplieres[i] = floor1Multiplier;
+		}
+
+		// 4.2.4.4. Residues
+		
+		int vorbisResidueCount = getLittleEndian(6) + 1;
+		
+		vorbisResidueTypes = new int[vorbisResidueCount];
+		residueBegin = new int[vorbisResidueCount];
+		residueEnd = new int[vorbisResidueCount];
+		residuePartitionSize = new int[vorbisResidueCount];
+		residueClassifications = new int[vorbisResidueCount];
+		residueClassbooks = new int[vorbisResidueCount];
+		residueMaximumPasses = new int[vorbisResidueCount];
+		
+		int residueCascadeEntry;
+		int classifications;
+		int typeEntry;
+		int maximumPassesEntry;
+		int passes;
+       	
+		residueCascade = new int[vorbisResidueCount][];
+		residueBooks = new int[vorbisResidueCount][][];
+		
+		for (i = 0; i < vorbisResidueCount; i++) {
+			maximumPassesEntry = 0;
+			typeEntry = getLittleEndian(16);
+			if (typeEntry > 2) {
+				throw new IOException("No support of residue type greater 2");
+			} else {
+				vorbisResidueTypes[i] = typeEntry;
+				residueBegin[i] = getLittleEndian(24);
+				residueEnd[i] = getLittleEndian(24);
+				residuePartitionSize[i] = getLittleEndian(24) + 1;
+				classifications = residueClassifications[i] = getLittleEndian(6) + 1;
+				
+				if ((residueClassbooks[i] = getLittleEndian(8)) >= vorbisCodebookCount) {
+					throw new IOException("Number of residue class books can't be greater than number of huffman books");
+				}
+
+				int[] cascade = residueCascade[i] = new int[classifications];
+				int[][] book = residueBooks[i] = new int[classifications][];
+				
+				for (j = 0; j < classifications; j++) {
+					residueCascadeEntry = getLittleEndian(3);
+					if (getLittleEndian1() == 1) {
+       					residueCascadeEntry |= getLittleEndian(5) << 3;
+					}
+					passes = ilog(residueCascadeEntry);
+					if (maximumPassesEntry < passes) {
+						maximumPassesEntry = passes;
+					}
+					book[j] = new int[passes];
+					cascade[j] = residueCascadeEntry;
+				}
+
+				residueMaximumPasses[i] = maximumPassesEntry;;
+
+				for (j = 0; j < classifications; j++) {
+					for (k = 0; k < book[j].length; k++) {
+						if ((cascade[j] & 1 << k) != 0) {
+							if ((book[j][k] = getLittleEndian(8)) >= vorbisCodebookCount) {
+								throw new IOException("Number of residue books can't be greater than number of huffman books");
+							}
+						}
+					}
+				}
+			}
+		}
+		
+		// 4.2.4.5. Mappings
+		
+		int vorbisMappingCount = getLittleEndian(6) + 1;
+		int vorbisMappingSubmaps;
+		int vorbisMappingSubmapsMax = -1;
+		int vorbisMappingCouplingSteps;
+		vorbisMappingMagnitude = new int[vorbisMappingCount][];
+		vorbisMappingAngle = new int[vorbisMappingCount][];
+		vorbisMappingMux = new int[vorbisMappingCount][channels];
+		vorbisMappingSubmapFloor = new int[vorbisMappingCount][];
+		vorbisMappingSubmapResidue = new int[vorbisMappingCount][];
+		int vorbisMappingMagnitudeEntry;
+		int vorbisMappingAngleEntry;
+		
+		for (i = 0; i < vorbisMappingCount; i++) {
+			if (getLittleEndian(16) != 0) {
+				throw new IOException("Wrong mapping type");
+			}
+			if (getLittleEndian1() == 1) {
+				vorbisMappingSubmaps = getLittleEndian(4) + 1;
+			} else {
+				vorbisMappingSubmaps = 1;
+			}
+			if (vorbisMappingSubmaps > vorbisMappingSubmapsMax) {
+				vorbisMappingSubmapsMax = vorbisMappingSubmaps;
+			}
+			if (getLittleEndian1() == 1) {
+				vorbisMappingCouplingSteps = getLittleEndian(8) + 1;
+				vorbisMappingMagnitude[i] = new int[vorbisMappingCouplingSteps];
+				vorbisMappingAngle[i] = new int[vorbisMappingCouplingSteps];
+				
+				for (j = 0; j < vorbisMappingCouplingSteps; j++) {
+					vorbisMappingMagnitudeEntry = getLittleEndian(ilog(channels - 1));
+					vorbisMappingAngleEntry = getLittleEndian(ilog(channels - 1));
+					if (vorbisMappingMagnitudeEntry == vorbisMappingAngleEntry || vorbisMappingMagnitudeEntry >= channels || vorbisMappingAngleEntry >= channels) {
+						throw new IOException("Wrong channel mapping");
+					}
+					
+					vorbisMappingMagnitude[i][j] = vorbisMappingMagnitudeEntry;
+					vorbisMappingAngle[i][j] = vorbisMappingAngleEntry;
+				}
+			} else {
+				vorbisMappingCouplingSteps = 0;
+			}
+			if (getLittleEndian(2) != 0) {
+				throw new IOException("Reserved bits should be zero");
+			}
+			if (vorbisMappingSubmaps > 1) {
+				for (j = 0; j < channels; j++) {
+					if ((vorbisMappingMux[i][j] = getLittleEndian(4)) >= vorbisMappingSubmapsMax) {
+						throw new IOException("Wrong channel mapping mux");
+					}
+				}
+			}
+			
+			int[] floor = vorbisMappingSubmapFloor[i] = new int[vorbisMappingSubmaps];
+			int[] residue = vorbisMappingSubmapResidue[i] = new int[vorbisMappingSubmaps];
+			
+			for (j = 0; j < vorbisMappingSubmaps; j++) {
+				skipLittleEndian(8);
+				if ((floor[j] = getLittleEndian(8)) >= vorbisFloorCount) {
+					throw new IOException("Wrong floor mapping number");
+				}
+				if ((residue[j] = getLittleEndian(8)) >= vorbisResidueCount) {
+					throw new IOException("Wrong residue mapping number");
+				}
+			}
+		}
+		
+		// 4.2.4.6. Modes
+		
+		int vorbisModeCount = getLittleEndian(6) + 1;
+		modeNumberBits = ilog(vorbisModeCount - 1);
+		vorbisModeBlockflag = new int[vorbisModeCount];
+		vorbisModeWindowtype = new int[vorbisModeCount];
+		vorbisModeTransformtype = new int[vorbisModeCount];
+		vorbisModeMapping = new int[vorbisModeCount];
+		
+		for (i = 0; i < vorbisModeCount; i++) {
+			vorbisModeBlockflag[i] = getLittleEndian1();
+			if ((vorbisModeWindowtype[i] = getLittleEndian(16)) > 0) {
+				throw new IOException("Wrong mode window type");
+			}
+			if ((vorbisModeTransformtype[i] = getLittleEndian(16)) > 0) {
+				throw new IOException("Wrong mode transform type");
+			}
+			if ((vorbisModeMapping[i] = getLittleEndian(8)) >= vorbisMappingCount) {
+				throw new IOException("Wrong mode mapping number");
+			}
+		}
+		if (getLittleEndian1() == 0) {
+			throw new IOException("Framing bit error");
+		}
+		headerInitialized3 = true;
+	}
+
+	public void readMediaInformation(Packet op) throws IOException, EndOfPacketException {
+
+		if(op == null) {
+			throw new InterruptedIOException("Packet is null");
+		}
+
+		loadPacket(op.packetBase, op.packet, op.bytes);
+		
+		byte[] buffer = new byte[6];
+		
+		int packetType = getLittleEndian(8);
+			
+		for (int i = 0; i < buffer.length; i++) {
+			buffer[i] = (byte) getLittleEndian(8);
+		}
+		
+		if (!new String(buffer).equals("vorbis")) {
+			throw new InterruptedIOException("No first packet");
+		}
+	
+		if (packetType == 0x01 && op.bos != 0) {
+			verifyFirstPacket();
+		} else if (packetType == 0x03 && op.bos == 0) {
+			verifySecondPacket();
+		} else if (packetType == 0x05 && op.bos == 0) {
+			verifyThirdPacket();
+		} else {
+			throw new InterruptedIOException("Wrong packet order");
+		}
+	}
+	
+	public final VorbisDecoder initializeVorbisDecoder() {
+		if (dec == null) {
+			dec = new VorbisDecoder(this);
+		}
+		return dec;
+	}
+	
+	public void forceReinitialization() {
+		if (dec != null) {
+			dec.close();
+			close();
+			dec = null;
+		}
+		headerInitialized1 = headerInitialized2 = headerInitialized3 = false;
+	}
+
+	/**
+	 * Returns one bit
+	 *
+	 * @return                               the integer value
+	 * @exception EndOfPacketExeption        if an end of packet occur
+	 */
+	public final int getLittleEndian1() throws EndOfPacketException {
+		if (packetByteIdx >= packetSize) {
+			throw new EndOfPacketException();
+		}
+
+		int val = data[byteIdx] >>> revBitIdx & 1;
+
+		revBitIdx++;
+		if (revBitIdx == BYTELENGTH) {
+			revBitIdx = 0;
+			byteIdx++;
+			packetByteIdx++;
+		}
+
+		return val;
+	}
+	
+	/**
+	 * Returns an integer with the length i
+	 *
+	 * @return                               the integer value
+	 * @param i                              the length in bits
+	 * @exception EndOfPacketExeption        if an end of packet occur
+	 */
+	public final int getLittleEndian(int i) throws EndOfPacketException {
+		if (i <= 0) {
+			return 0;
+		}
+		if (packetByteIdx >= packetSize) {
+			throw new EndOfPacketException();
+		}
+
+		int store = revBitIdx;
+		
+		int val = (data[byteIdx] & 0xFF) >>> store;
+		
+		revBitIdx += i;
+		
+		if (revBitIdx >= BYTELENGTH) {
+			byteIdx++;
+			if (++packetByteIdx >= packetSize) {
+				throw new EndOfPacketException();
+			}
+			val |= (data[byteIdx] & 0xFF) << BYTELENGTH - store;
+			if (revBitIdx >= DOUBLE_BYTELENGTH) {
+				byteIdx++;
+				if (++packetByteIdx >= packetSize) {
+					throw new EndOfPacketException();
+				}
+				val |= (data[byteIdx] & 0xFF) << DOUBLE_BYTELENGTH - store;
+				if (revBitIdx >= TRIPLE_BYTELENGTH) {
+					byteIdx++;
+					if (++packetByteIdx >= packetSize) {
+						throw new EndOfPacketException();
+					}
+					val |= (data[byteIdx] & 0xFF) << TRIPLE_BYTELENGTH - store;
+					if (revBitIdx >= QUADRUPEL_BYTELENGTH) {
+						byteIdx++;
+						if (++packetByteIdx >= packetSize) {
+							throw new EndOfPacketException();
+						}
+						val |= ((data[byteIdx] & 0xFF) << QUADRUPEL_BYTELENGTH - store) & 0xFF000000;
+					}
+				}
+			}
+			revBitIdx &= 7;
+		}
+		return val & BITMASK[i];
+	}
+	
+	final void skipLittleEndian(int j) throws EndOfPacketException {
+		if (packetByteIdx >= packetSize) {
+			throw new EndOfPacketException();
+		}
+		revBitIdx += j;
+		while (revBitIdx >= BYTELENGTH) {
+			revBitIdx -= BYTELENGTH;
+			byteIdx++;
+			if (++packetByteIdx >= packetSize) {
+				throw new EndOfPacketException();
+			}
+		}
+	}
+
+	public final static int ilog(int v) {
+    	int ret = 0;
+		
+		while (v != 0) {
+			ret++;
+			v >>>= 1;
+		}
+		return ret;
+	}
+	
+	private static float float32Unpack(int x) {
+		int mantissa = x & 0x1FFFFF;
+		int exponent = (x & 0x7FE00000) >>> 21;
+		
+		if ((x & 0x80000000) != 0) {
+			mantissa = -mantissa;
+		}
+		return (float) (mantissa * Math.pow(2, exponent - 788));
+	}
+
+  	public String getOggCommentContent() {
+		return tag.toString();
+	}
+	
+	final int getCodeWord(int bookNumber) throws IOException, EndOfPacketException {
+		
+		HuffTreeEntry node = hts[bookNumber];
+		
+		if (node == null) {
+			throw new IOException("Unexcepted end of codebook");
+		}
+		if (node.sparse) {
+			return node.value;
+		}
+		while (node.value == -1) {
+			node = node.childFeed[getLittleEndian(node.feed)];
+			if (node == null) {
+				throw new IOException("Unexcepted end of codebook");
+			}
+		}
+		return node.value;
+	}
+
+	final boolean bookIsUnused(int bookNumber) {
+		return hts[bookNumber] == null ? true : false;
+	}
+
+	/**
+	 * Frees all system resources, which are bounded to this object.
+	 */
+	public void close() {
+		int i, j;
+		
+		if (hts != null) {
+			for (i = 0; i < hts.length; i++) {
+				if (hts[i] != null) {
+					closeTree(hts[i]);
+				}
+				hts[i] = null;
+			}
+		} else {
+			return;
+		}
+		tag.close();
+		tag = null;
+		
+		hts = null;
+		data = null;
+		for (i = 0; i < valueVector.length; i++) {
+			valueVector[i] = null;
+		}
+		valueVector = null;
+		codebookDimensions = null;
+		vorbisFloorTypes = null;
+		floor1Multiplieres = null;
+		for (i = 0; i < floor1PartitionClassList.length; i++) {
+			floor1PartitionClassList[i] = null;
+		}
+		floor1PartitionClassList = null;
+		for (i = 0; i < floor1ClassDimensions.length; i++) {
+			floor1ClassDimensions[i] = null;
+		}
+		floor1ClassDimensions = null;
+		for (i = 0; i < floor1ClassSubclasses.length; i++) {
+			floor1ClassSubclasses[i] = null;
+		}
+		floor1ClassSubclasses = null;
+		for (i = 0; i < floor1ClassMasterbooks.length; i++) {
+				floor1ClassMasterbooks[i] = null;
+		}
+		floor1ClassMasterbooks = null;
+		for (i = 0; i < floor1XList.length; i++) {
+			floor1XList[i] = null;
+		}
+		floor1XList = null;
+		
+		int[][] pointer;
+		
+		for (i = 0; i < floor1SubclassBooks.length; i++) {
+			pointer = floor1SubclassBooks[i];
+			for (j = 0; j < pointer.length; j++) {
+				pointer[j] = null;
+			}
+			pointer = null;
+		}
+		floor1SubclassBooks = null;
+		vorbisResidueTypes = null;
+		residueMaximumPasses = null;
+		residueBegin = null;
+		residueEnd = null;
+		residuePartitionSize = null;
+		residueClassifications = null;
+		residueClassbooks = null;
+		for (i = 0; i < residueCascade.length; i++) {
+			residueCascade[i] = null;
+		}
+		residueCascade = null;
+		for (i = 0; i < residueBooks.length; i++) {
+			pointer = residueBooks[i];
+			for (j = 0; j < pointer.length; j++) {
+				pointer[j] = null;
+			}
+			pointer = null;
+		}
+		residueBooks = null;
+		for (i = 0; i < vorbisMappingMagnitude.length; i++) {
+			vorbisMappingMagnitude[i] = null;
+		}
+		vorbisMappingMagnitude = null;
+		for (i = 0; i < vorbisMappingAngle.length; i++) {
+			vorbisMappingAngle[i] = null;
+		}
+		vorbisMappingAngle = null;
+		for (i = 0; i < vorbisMappingMux.length; i++) {
+			vorbisMappingMux[i] = null;
+		}
+		vorbisMappingMux = null;
+		for (i = 0; i < vorbisMappingSubmapFloor.length; i++) {
+			vorbisMappingSubmapFloor[i] = null;
+		}
+		vorbisMappingSubmapFloor = null;
+		for (i = 0; i < vorbisMappingSubmapResidue.length; i++) {
+			vorbisMappingSubmapResidue[i] = null;
+		}
+		vorbisMappingSubmapResidue = null;
+		vorbisModeBlockflag = null;
+		vorbisModeWindowtype = null;
+		vorbisModeTransformtype = null;
+		vorbisModeMapping = null;
+	}
+	
+	public int blocksize(Packet op){
+    	int modeNumber;
+ 
+    	loadPacket(op.packetBase, op.packet, op.bytes);
+		
+		try {
+			if (getLittleEndian1() != 0) {
+				return -1;
+			}
+		
+			modeNumber = getLittleEndian(modeNumberBits);
+			
+		} catch (Exception e) {
+			return -1;
+		}
+		return blocksizes[vorbisModeBlockflag[modeNumber]];
+	}
+
+	private void deflateTree(HuffTreeEntry node) throws IOException {
+		int i, j, k, l, r, feedMinusOne, feedMinusOneMinusJ;
+		HuffTreeEntry nodeBase = node, nodeVerify;
+			
+		for (node.feed = 2; node.feed < 33; node.feed++) {
+			k = 1 << node.feed;
+			HuffTreeEntry[] copy = new HuffTreeEntry[k];
+			
+			feedMinusOne = node.feed - 1;
+			for (i = 0; i < k; i++) {
+				for (j = 0; j < node.feed; j++) {
+					feedMinusOneMinusJ = feedMinusOne - j;
+					l = (i & 1 << feedMinusOneMinusJ) >>> feedMinusOneMinusJ;
+					nodeVerify = nodeBase.child[l ^ 1];
+					nodeBase = nodeBase.child[l];
+					if (nodeBase == null) {
+						node.feed--;
+						copy = null;
+						if (node.feed > 1) {
+							for (i = 0; i < node.childFeed.length; i++) {
+								deflateTree(node.childFeed[i]);
+							}
+						}
+						return;
+					}
+					if (nodeVerify == null) {
+						throw new InterruptedIOException("Underpopulated tree");
+					}
+				}
+				r = 0;
+				for (j = 0; j < node.feed; j++) {
+					r <<= 1;
+					r |= i >>> j & 1;
+				}
+				copy[r] = nodeBase;
+				nodeBase = node;
+			}
+			for (i = 0; i < node.childFeed.length; i++) {
+				node.childFeed[i].dereferenced = true;
+			}
+			node.childFeed = copy;
+		}
+	}
+	
+	private void buildTree(HuffTreeEntry node, int maxLength) throws IOException {
+		int i, j;
+		int shiftedNodeCount;
+		int limit = maxLength + 1;
+		int[] shiftedNodeCounts = new int[limit];
+		int currentLength;
+		int currentValue;
+		HuffTreeEntry nodeBase = node;
+			
+		for (i = 0; i < codebookCodewordLengths.length; i++) {
+			if ((currentLength = codebookCodewordLengths[i]) == 0) {
+				continue;
+			}
+			currentValue = shiftedNodeCount = shiftedNodeCounts[currentLength];
+			if (shiftedNodeCount >>> currentLength != 0) {
+				throw new InterruptedIOException("Overpopulated tree");
+			}
+			for (j = currentLength; j > 0; j--) {
+				if ((shiftedNodeCounts[j] & 1) != 0) {
+					if (j == 1) {
+						shiftedNodeCounts[1]++;
+					} else {
+						shiftedNodeCounts[j] = shiftedNodeCounts[j - 1] << 1;
+					}
+					break;
+				}
+				shiftedNodeCounts[j]++;
+			}
+			for (j = currentLength + 1; j < limit; j++) {
+				if ((shiftedNodeCounts[j] >>> 1) == shiftedNodeCount) {
+					shiftedNodeCount = shiftedNodeCounts[j];
+					shiftedNodeCounts[j] = shiftedNodeCounts[j - 1] << 1;
+				} else {
+					break;
+				}
+			}
+			for (j = currentLength - 1; j >= 0; j--) {
+				if ((currentValue >>> j & 0x1) == 1) {
+					if (nodeBase.child[1] == null) {
+						nodeBase.child[1] = new HuffTreeEntry();
+					}
+					nodeBase = nodeBase.child[1];
+				} else {
+					if (nodeBase.child[0] == null) {
+						nodeBase.child[0] = new HuffTreeEntry();
+					}
+					nodeBase = nodeBase.child[0];
+				}
+			}
+			nodeBase.value = i;
+			nodeBase = node;
+		}
+	}
+
+	private void pruneTree(HuffTreeEntry node) {
+		HuffTreeEntry left = node.child[0];
+		HuffTreeEntry right = node.child[1];
+		
+		if (left != null) {
+			pruneTree(left);
+			pruneTree(right);
+		}
+		if (node.dereferenced) {
+			node.child = null;
+			node = null;
+		} else {
+			if (node.child != node.childFeed) {
+				node.child = null;
+			}
+		}
+	}
+	
+	private void closeTree(HuffTreeEntry node) {
+		if (node.sparse) {
+			node.child = null;
+			node = null;
+			return;
+		}
+
+		HuffTreeEntry nodeBase;
+		
+		for (int i = 0; i < node.childFeed.length; i++) {
+			nodeBase = node.childFeed[i];
+			if (nodeBase != null) {
+				closeTree(nodeBase);
+				nodeBase = null;
+			}
+		}
+		node.child = null;
+		node.childFeed = null;
+		node = null;
+	}
+	
+	private class HuffTreeEntry {
+		HuffTreeEntry[] child = new HuffTreeEntry[2];
+		HuffTreeEntry[] childFeed = child;
+		int value = -1;
+		boolean sparse;
+		byte feed = 1;
+		boolean dereferenced;
+	}
+}
+
+

Added: trunk/cronus/com/meviatronic/zeus/castor/EndOfMediaException.java
===================================================================
--- trunk/cronus/com/meviatronic/zeus/castor/EndOfMediaException.java	                        (rev 0)
+++ trunk/cronus/com/meviatronic/zeus/castor/EndOfMediaException.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,45 @@
+/* Castor, a fast Vorbis decoder created by Michael Scheerer.
+ *
+ * Castor decoder (c) 2010 Michael Scheerer www.meviatronic.com
+ *
+ * Many thanks to
+ *   Monty <monty at xiph.org> and
+ *   The XIPHOPHORUS Company http://www.xiph.org/ .
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+   
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+package com.meviatronic.zeus.castor;
+
+import java.io.*;
+
+/**
+ * This <code>Exception</code> should be thrown if an end of media exception occurs.
+ *
+ * @author    Michael Scheerer
+ */
+public final class EndOfMediaException extends IOException {
+
+	/**
+	 * Constructs an instance of <code>EndOfMediaException</code>.
+	 *
+	 * @param s  the exception message
+	 */
+	public EndOfMediaException(java.lang.String s) {
+		super(s);
+	}
+}
+

Added: trunk/cronus/com/meviatronic/zeus/castor/EndOfPacketException.java
===================================================================
--- trunk/cronus/com/meviatronic/zeus/castor/EndOfPacketException.java	                        (rev 0)
+++ trunk/cronus/com/meviatronic/zeus/castor/EndOfPacketException.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,34 @@
+/* Castor, a fast Vorbis decoder created by Michael Scheerer.
+ *
+ * Castor decoder (c) 2010 Michael Scheerer www.meviatronic.com
+ *
+ * Many thanks to
+ *   Monty <monty at xiph.org> and
+ *   The XIPHOPHORUS Company http://www.xiph.org/ .
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+   
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+package com.meviatronic.zeus.castor;
+
+/**
+ * This <code>Exception</code> should be thrown if an end of packet exception occurs.
+ *
+ * @author    Michael Scheerer
+ */
+public final class EndOfPacketException extends Exception {
+}
+

Added: trunk/cronus/com/meviatronic/zeus/castor/Imdct.java
===================================================================
--- trunk/cronus/com/meviatronic/zeus/castor/Imdct.java	                        (rev 0)
+++ trunk/cronus/com/meviatronic/zeus/castor/Imdct.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,300 @@
+/* Castor, a fast Vorbis decoder created by Michael Scheerer.
+ *
+ * Castor decoder (c) 2010 Michael Scheerer www.meviatronic.com
+ *
+ * Many thanks to
+ *   Monty <monty at xiph.org> and
+ *   The XIPHOPHORUS Company http://www.xiph.org/ .
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+   
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+package com.meviatronic.zeus.castor;
+
+/**
+ *  The <code>Imdct</code> class provides an implementation of the inverse MDCT
+ *  based on:
+ *  The use of multirate filter banks for coding of high quality digital audio,
+ *  6th European Signal Processing Conference (EUSIPCO), Amsterdam, June 1992, Vol.1, pages 211-214Th,
+ *  Sporer Kh.Brandenburg B.Edler
+ *
+ *  @author  Michael Scheerer
+ */
+final class Imdct {
+	private float[] A;
+	private float[] AR;
+	private float[] BH;
+	private float[] C;
+	private int[] bitreversedIndex;
+	private int n;
+	private int nHalf;
+	private int nQuarter;
+	private int nThreeFourths;
+	private int nEighth;
+	private int lEnd;
+ 
+	void initialize(int n) {
+	
+		this.n = n;
+		
+		int i, j, k = 0;
+		
+		nHalf = n >>> 1;
+		nQuarter = n >>> 2;
+		nEighth = n >>> 3;
+		nThreeFourths = nQuarter * 3;
+		A = new float[nHalf];
+		AR = new float[nHalf];
+		BH = new float[nHalf];
+		C = new float[nQuarter];
+		bitreversedIndex = new int[nEighth];
+	
+		int log2n = (int) (Math.log(n) / Math.log(2));
+	  
+	  	lEnd = log2n - 3;
+	  
+		int codePattern;
+		
+		for (i = 0; i < nEighth; i++) {
+			codePattern = 0;
+			
+			for (j = 0; j < lEnd; j++) {
+				codePattern <<= 1;
+				codePattern |= i >>> j & 1;
+			}
+			
+			j = codePattern;
+			
+			if (!(i > 0 && i < nEighth - 1)) {
+				j = i;
+			}
+			bitreversedIndex[i] = j;
+		}
+		
+		int nHalfMinusI2Minus1;
+		int nHalfMinusI2Minus2;
+		int i2, i2Plus1;
+		
+	  	for (i = 0; i < nQuarter; i++) {
+		  	i2 = i << 1;
+		  	i2Plus1 = i2 + 1;
+			nHalfMinusI2Minus1 = nHalf - i2 - 1;
+			nHalfMinusI2Minus2 = nHalf - i2 - 2;
+			AR[nHalfMinusI2Minus1] = A[i2] = (float) Math.cos(Math.PI / n * 4 * i);
+			AR[nHalfMinusI2Minus2] = A[i2Plus1] = (float) -Math.sin(Math.PI / n * 4 * i);
+			BH[i2] = (float) Math.cos(Math.PI / (n << 1) * (i << 1 | 1)) / 2;
+			BH[i2Plus1] = (float) Math.sin(Math.PI / (n << 1) * (i << 1 | 1)) / 2;
+		}
+
+	  	for (i = 0; i < nEighth; i++) {
+		  	i2 = i << 1;
+			C[i2] = (float) Math.cos(Math.PI / nHalf * (i << 1 | 1));
+			C[i2 + 1] = (float) -Math.sin(Math.PI / nHalf * (i << 1 | 1));
+		}
+	}
+	
+	void decode(float[] in, float[] out, float[] window, float[] floor) {
+		int k, l, r, s;
+		float upOdd, downOdd, upEven, downEven;
+		int upOddIndex, downOddIndex, upEvenIndex, downEvenIndex;
+ 		int i2k, i2kPlus1, i2kPlusNquarter, i2kPlus1PlusNquarter;
+ 		int i4k, i4kr, i4kPlus1, i4kPlus3;
+		int iNquarterMinuskMinus1, iNthreeFourthsMinuskMinus1, iNthreeFourthsPlusk, iNquarterPlusk;
+ 		int iNquarterMinus1 = nQuarter - 1, iNthreeFourthsMinus1 = nThreeFourths - 1;
+ 		int iNhalfMinus1 = nHalf - 1, iNhalfMinus2 = nHalf - 2, iNhalfMinus4 = nHalf - 4;
+ 		int iNhalfMinus4kMinus4, iNhalfMinus4kMinus4r;
+		int iNhalfMinus2Minusr2;
+		int k0, k0Half, k1;
+		int rEnd, sEnd;
+		int rk1;
+		float ark1, ark1Plus1;
+		float ar2k, ar2kPlus1, ar2kPlusNhalf, ar2kPlus1PlusNhalf, ar4k, ar4kPlus1;
+ 		float factor1, factor2, factor3, factor4, factor5, factor6;
+		float base1, base2;
+		float w4k, w4kPlus1, w4kPlus3;
+		float wNhalfMinus4kMinus1, wNhalfMinus4kMinus2, wNhalfMinus4kMinus4;
+		float c2k, c2kPlus1;
+		float bh2k, bh2kPlus1;
+		
+		// First half:
+		// Vn-4k-1 = 2 * (U4k * A2k - U4k+2 * A2k+1)
+		// Vn-4k-3 = 2 * (U4k * A2k+1 + U4k+2 * A2k)
+
+		// Second half:
+		// Vn-4k-1 = 2 * (-Un-4k-1 * A2k + Un-4k-3 * A2k+1)
+		// Vn-4k-3 = 2 * (-Un-4k-1 * A2k+1 - Un-4k-3 * A2k)
+		
+		// First half mirrored index:
+		// V4k+3 = 2 * (Un-4k-4 * Ar2k - Un-4k-2 * Ar2k+1)
+		// V4k+1 = 2 * (Un-4k-4 * Ar2k+1 + Un-4k-2 * Ar2k)
+		
+		// Endresult:
+		// First half mirrored index mirrored array:
+		// V4k+3 = 2 * (-U4k+3 * Ar2k + U4k+1 * Ar2k+1)
+		// V4k+1 = 2 * (-U4k+3 * Ar2k+1 - U4k+1 * Ar2k)
+
+		// Second half mirrored index:
+		// V4k+3 = 2 * (-U4k+3 * Ar2k + U4k+1 * Ar2k+1)
+		// V4k+1 = 2 * (-U4k+3 * Ar2k+1 - U4k+1 * Ar2k)
+		
+		// Second half mirrored index plus nHalf:
+		// V4k+3+nHalf = 2 * (-U4k+3+nHalf * Ar2k+nHalf + U4k+1+nHalf * Ar2k+1+nHalf)
+		// V4k+1+nHalf = 2 * (-U4k+3+nHalf * Ar2k+1+nHalf - U4k+1+nHalf * Ar2k+nHalf)
+		
+		// Endresult:
+		// Second half mirrored index mirrored array plus nHalf:
+		// V4k+3+nHalf = 2 * (UnHalf-4k-4 * Ar2k+nHalf - UnHalf-4k-2 * Ar2k+1+nHalf)
+		// V4k+1+nHalf = 2 * (UnHalf-4k-4 * Ar2k+1+nHalf + UnHalf-4k-2 * Ar2k+nHalf)
+		
+		// W4k+3+nHalf = V4k+3+nHalf + V4k+3
+		// W4k+1+nHalf = V4k+1+nHalf + V4k+1
+		// W4k+3 = (V4k+3+nHalf - V4k+3) * Ar4k - (V4k+1+nHalf - V4k+1) * Ar4k+1
+		// W4k+1 = (V4k+1+nHalf - V4k+1) * Ar4k - (V4k+3+nHalf - V4k+3) * Ar4k+1
+		
+		for (k = 0; k < nEighth; k++) {
+			i4k = k << 2;
+			i2k = k << 1;
+			i2kPlus1 = i2k | 1;
+			i4kPlus1 = i4k | 1;
+			i4kPlus3 = i4k | 3;
+			i2kPlusNquarter = i2k | nQuarter;
+			i2kPlus1PlusNquarter = i2kPlus1 | nQuarter;
+			iNhalfMinus4kMinus4 = iNhalfMinus4 - i4k;
+			w4kPlus1 = in[i4kPlus1] * floor[i4kPlus1];
+			w4kPlus3 = in[i4kPlus3] * floor[i4kPlus3];
+			wNhalfMinus4kMinus4 = in[iNhalfMinus4kMinus4] * floor[iNhalfMinus4kMinus4];
+			wNhalfMinus4kMinus2 = in[iNhalfMinus4kMinus4 | 2] * floor[iNhalfMinus4kMinus4 | 2];
+			ar2k = AR[i2k];
+			ar2kPlus1 = AR[i2kPlus1];
+			ar2kPlusNhalf = AR[i2kPlusNquarter];
+			ar2kPlus1PlusNhalf = AR[i2kPlus1PlusNquarter];
+			ar4k = AR[i4k];
+			ar4kPlus1 = AR[i4kPlus1];
+			factor1 = -w4kPlus3 * ar2kPlus1 + w4kPlus1 * ar2k; // V4k+3
+			factor2 = -w4kPlus3 * ar2k - w4kPlus1 * ar2kPlus1; // V4k+1
+			factor3 = wNhalfMinus4kMinus4 * ar2kPlus1PlusNhalf - wNhalfMinus4kMinus2 * ar2kPlusNhalf; // V4k+3+nHalf
+			factor4 = wNhalfMinus4kMinus4 * ar2kPlusNhalf + wNhalfMinus4kMinus2 * ar2kPlus1PlusNhalf; // V4k+1+nHalf
+			out[i2kPlus1PlusNquarter] = factor1 + factor3;
+			out[i2kPlusNquarter] = factor2 + factor4;
+			factor5 = factor3 - factor1;
+			factor6 = factor4 - factor2;
+			out[i2kPlus1] = factor5 * ar4kPlus1 - factor6 * ar4k;
+			out[i2k] =      factor5 * ar4k + factor6 * ar4kPlus1;
+		}
+
+		for (l = 0; l < lEnd; l++) {
+			k0 = n >>> l + 2;
+			k1 = 1 << l + 3;
+			k0Half = k0 >>> 1;
+			rEnd = k0 >>> 2;
+			sEnd = k1 >>> 2;
+			for (r = 0; r < rEnd; r++) {
+				iNhalfMinus2Minusr2 = iNhalfMinus2 - (r << 1);
+				ark1 = A[rk1 = r * k1];
+				ark1Plus1 = A[rk1 | 1];
+				for (s = 0; s < sEnd; s++) {
+					downEvenIndex = iNhalfMinus2Minusr2 - k0 * s;
+					downOddIndex = downEvenIndex - k0Half;
+					upEven = out[upEvenIndex = downEvenIndex | 1];
+					upOdd = out[upOddIndex = downOddIndex | 1];
+					downEven = out[downEvenIndex];
+					downOdd = out[downOddIndex];
+					out[upEvenIndex] = upEven + upOdd;
+					out[downEvenIndex] = downEven + downOdd;
+					factor5 = upEven - upOdd;
+					factor6 = downEven - downOdd;
+					out[upOddIndex] =   factor5 * ark1 - factor6 * ark1Plus1;
+					out[downOddIndex] = factor5 * ark1Plus1 + factor6 * ark1;
+				}
+ 			}
+		}
+		
+		// Un-2k-1 = W4k
+		// Un-2k-2 = W4k+1
+		// UnThreeFourths-2k-1 = W4k+2
+		// UnThreeFourths-2k-2 = W4k+3
+
+		// Partial mirrored indices:
+		// Un-2k-1 = W4k
+		// Un-2k-2 = W4k+1
+		// U2k+1+nHalf = WnHalf-4k-2
+		// U2k+nHalf = WnHalf-4k-1
+
+		// Faktor1 = (WnHalf-4k-1 + W4k+1)
+		// Faktor2 = (WnHalf-4k-1 - W4k+1) * C2k+1
+		// Faktor3 = (WnHalf-4k-2 + W4k  ) * C2k
+		// Faktor4 = (WnHalf-4k-2 - W4k  )
+		// Faktor5 = (WnHalf-4k-2 + W4k  ) * C2k+1
+		// Faktor6 = (WnHalf-4k-1 - W4k+1) * C2k
+
+		// V2k+nHalf    =  Faktor1+Faktor2+Faktor3
+		// Vn-2k-2      =  Faktor1-Faktor2-Faktor3
+		// V2k+1+nHalf  =  Faktor4+Faktor5-Faktor6
+		// Vn-2k-1      = -Faktor4+Faktor5-Faktor6
+		
+		for (k = 0; k < nEighth; k++) {
+			i4k = k << 2;
+			i4kr = bitreversedIndex[k] << 2;
+			iNhalfMinus4kMinus4r = iNhalfMinus4 - i4kr;
+			i2k = k << 1;
+			c2k = C[i2k];
+			c2kPlus1 = C[i2k | 1];
+			w4k = out[i4kr];
+			w4kPlus1 = out[i4kr | 1];
+			wNhalfMinus4kMinus1 = out[iNhalfMinus4kMinus4r | 3];
+ 			wNhalfMinus4kMinus2 = out[iNhalfMinus4kMinus4r | 2];
+ 			base1 = wNhalfMinus4kMinus1 - w4kPlus1;
+			base2 = wNhalfMinus4kMinus2 + w4k;
+ 			factor1 =  wNhalfMinus4kMinus1 + w4kPlus1;
+ 			factor2 = base1 * c2kPlus1;
+ 			factor3 = base2 * c2k;
+			factor4 =  wNhalfMinus4kMinus2 - w4k;
+			factor5 = base2 * c2kPlus1;
+ 			factor6 = base1 * c2k;
+			in[i2k]					=  factor1 + factor2 + factor3;
+			in[iNhalfMinus2 - i2k]   =  factor1 - factor2 - factor3;
+			in[i2k | 1]				=  factor4 + factor5 - factor6;
+			in[iNhalfMinus1 - i2k]   = -factor4 + factor5 - factor6;
+		}
+		
+		for (k = 0; k < nQuarter; k++) {
+			i2k = k << 1;
+ 			i2kPlus1 = i2k | 1;
+ 			iNquarterMinuskMinus1 = iNquarterMinus1 - k;
+			iNthreeFourthsMinuskMinus1 = iNthreeFourthsMinus1 - k;
+ 			iNthreeFourthsPlusk = nThreeFourths | k;
+			iNquarterPlusk = nQuarter | k;
+			bh2k = BH[i2k];
+			bh2kPlus1 = BH[i2kPlus1];
+			factor5 = in[i2k];
+			factor6 = in[i2kPlus1];
+			factor1 = factor5 * bh2k + factor6 * bh2kPlus1;
+			factor2 = factor5 * bh2kPlus1 - factor6 * bh2k;
+			out[iNquarterMinuskMinus1] = factor2 * window[iNquarterMinuskMinus1];
+			out[iNthreeFourthsPlusk] = -factor1 * window[iNthreeFourthsPlusk];
+			out[iNquarterPlusk] = -factor2 * window[iNquarterPlusk];
+ 			out[iNthreeFourthsMinuskMinus1] = -factor1 * window[iNthreeFourthsMinuskMinus1];
+		 }
+	}
+
+ 	void close() {
+		A = null;
+		AR = null;
+		BH = null;
+		C = null;
+		bitreversedIndex = null;
+	}
+}

Added: trunk/cronus/com/meviatronic/zeus/castor/Output.java
===================================================================
--- trunk/cronus/com/meviatronic/zeus/castor/Output.java	                        (rev 0)
+++ trunk/cronus/com/meviatronic/zeus/castor/Output.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,170 @@
+/* Castor, a fast Vorbis decoder created by Michael Scheerer.
+ *
+ * Castor decoder (c) 2010 Michael Scheerer www.meviatronic.com
+ *
+ * Many thanks to
+ *   Monty <monty at xiph.org> and
+ *   The XIPHOPHORUS Company http://www.xiph.org/ .
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+   
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+package com.meviatronic.zeus.castor;
+
+import org.xiph.ogg.*;
+
+import java.io.*;
+
+/**
+ * The <code>Output</code> class implements all output related methods of the decoder.
+ * Furthermore all output channel related settings are performed
+ *
+ * @author    Michael Scheerer
+ */
+abstract public class Output {
+
+  	AudioReader info;
+	int channels;
+	byte[] buffer; // inlining
+	int[] pointer; // inlining
+	
+	private int frameBufferSize;
+	private int frameBufferSizePerChannel;
+	private long granulepos;
+	private long lastGranulepos;
+	private int sampleOffset;
+	private boolean eom;
+	private boolean bom;
+  	
+	Output(AudioReader info){
+		
+		this.info = info;
+		
+		channels = info.channels;
+		
+		frameBufferSizePerChannel = info.blocksizes[1];
+		int frameBufferSize = frameBufferSizePerChannel * channels;
+
+		buffer = new byte[frameBufferSize];
+		pointer = new int[channels];
+	}
+
+	abstract void decode() throws IOException, EndOfMediaException, EndOfPacketException;
+	
+	public final byte[] synthesis(Packet op) throws IOException, EndOfPacketException {
+    	info.loadPacket(op.packetBase, op.packet, op.bytes);
+		
+		eom = op.eos == 1 ? true : false;
+		bom = op.bos == 1 ? true : false;
+
+		resetBufferPointer();
+		
+		decode();
+		
+		if (op.granulepos == -1) {
+			granulepos = lastGranulepos + frameBufferSizePerChannel;
+		} else {
+			granulepos = op.granulepos;
+		}
+		sampleOffset = 0;
+		if (granulepos < 0 && bom) {
+			frameBufferSizePerChannel += granulepos;
+			sampleOffset = (int) -granulepos;
+			if (frameBufferSizePerChannel < 0) {
+				frameBufferSizePerChannel = 0;
+			}
+		}
+		if (granulepos - lastGranulepos < frameBufferSizePerChannel && eom) {
+			frameBufferSizePerChannel = (int) (granulepos - lastGranulepos);
+		}
+		lastGranulepos = granulepos;
+		return buffer;
+	}
+	
+  	public final int getNumberOfSamples() {
+		return frameBufferSizePerChannel;
+  	}
+	
+	public final int getSampleOffset() {
+		return sampleOffset;
+	}
+
+  	/**
+	 * Frees all system resources, which are bounded to this object.
+	 */
+	public void close() {
+		int i;
+		
+		buffer = null;
+		pointer = null;
+	}
+	
+	final void setBlockSize(int bufferSize, int spectrumSize) {
+		frameBufferSizePerChannel = bufferSize << 1;
+		frameBufferSize = frameBufferSizePerChannel * channels;
+	}
+	
+	final void setBuffer(float f, int channelNumber) {
+		
+		if (f >= 1) {
+			f = 1;
+		}
+		if (f <= -1) {
+			f = -1;
+		}
+	
+		f *= 32767;
+
+		short w = (short) f;
+		// one short with big-endian order to 2 bytes in big-endian order!!
+		byte b1 = (byte) (w >>> 8);
+		byte b2 = (byte) w;
+		
+		int p = pointer[channelNumber];
+		
+		buffer[p] = b1;
+		buffer[p + 1] = b2;
+			
+		p += channels << 1;
+		pointer[channelNumber] = p;
+	}
+	
+	private void resetBufferPointer() {
+		for (int i = 0; i < channels; i++) {
+			pointer[i] = 2 * i;
+		}
+	}
+	
+	final boolean bookIsUnused(int bookNumber) {
+		return info.bookIsUnused(bookNumber);
+	}
+	
+	final int get1() throws EndOfPacketException {
+		return info.getLittleEndian1();
+	}
+	
+	final int get(int i) throws EndOfPacketException {
+		return info.getLittleEndian(i);
+	}
+	 
+	final int getCodeWord(int bookNumber) throws IOException, EndOfPacketException {
+		return info.getCodeWord(bookNumber);
+	}
+	
+	final static int ilog(int v) {
+		return AudioReader.ilog(v);
+	}
+}

Added: trunk/cronus/com/meviatronic/zeus/castor/README.txt
===================================================================
--- trunk/cronus/com/meviatronic/zeus/castor/README.txt	                        (rev 0)
+++ trunk/cronus/com/meviatronic/zeus/castor/README.txt	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,94 @@
+	Castor, a fast Vorbis decoder created by Michael Scheerer.
+	Castor decoder (c) 2009 Michael Scheerer www.meviatronic.com
+
+	Many thanks to
+	Monty <monty at xiph.org> and
+	The XIPHOPHORUS Company http://www.xiph.org/ .
+	
+	Castor's IMDCT based on:
+	The use of multirate filter banks for coding of high quality digital audio,
+	6th European Signal Processing Conference (EUSIPCO), Amsterdam, June 1992, Vol.1, pages 211-214Th,
+	Sporer Kh.Brandenburg B.Edler
+	
+	Features:
+ 	- 9 loops and one threefold loop.
+	
+	Features of Castor's enhanced IMDCT:
+	- inplacing inside the performance critical part.
+ 	- 3 loops and one threefold nested loop.
+ 	- includes windowing.
+ 	- includes dot product.
+	- 2 loops plus a windowing loop less than the IMDCT of the current reference decoder.
+	
+	Castor's CRC32 based on (but here used inside the Ogg Framing):
+	public domain code by
+	Ross Williams (ross at guest.adelaide.edu.au).
+	A 32 bit CRC value (direct algorithm, initial val and final XOR = 0, generator polynomial=0x04c11db7) is used.
+	The value is computed over the entire header (with the CRC field in the header set to zero)
+	and then continued over the page. The CRC field is then filled with the computed value.
+
+	Castor's huffman decoding based on:
+ 	Self developed algorithm (deflated binary tree approach -
+	a kind of a 2 pow n compression), therefore there's no reference.
+	Dr. m. m. Timothy B. Terriberry pointed out, that this kind of approach is at least since 1993 a
+	reinvention:
+	R. Hashemian, "High Speed Search and Memory Efficient
+    Huffman Coding," 1993.
+	
+	Castor's bitstream decoding based on:
+ 	Self developed algorithm, therefore there's no reference.
+
+	Summary:
+	
+	Advantages of Castor over the current reference decoder (libVorbis 1.2.3):
+	- in some technical aspects better IMDCT.
+	- drastical reduced binary or bytecode size
+ 	  (from 50 Kbyte Java bytecode ripped of floor 0 and encoder stuff to only 35 Kbyte including Helena).
+ 	- support of VQ lookup type 0 (the spec describes type 0 ambigiously as
+ 	  indirectly allowed and forbidden).
+ 	- support of all specified truncated packet operations.
+ 	- support of codebooks with a single used entry.
+ 	
+ 	Advantages of Castor over an older Java version of the reference decoder:
+	- memory consumption is lower (under Java according to the taskmanager).
+	- performance consumption is lower (under Java according to the taskmanager).
+ 	
+ 	The decoder itself supports full gapless playerback and positive time offset (broadcaststream).
+ 	
+ 	The code size and/or memory consumption may be of interest for
+ 	firmware developers of hardware players.
+ 	
+ 	A firmware port of Castor should replace the recursions during the
+ 	Huffman decoder initialization. Under Java, this recursion based initialization
+ 	is faster than the non recursion version.
+ 	
+ 	Todo:
+ 	
+ 	Exact performance tests against the actual reference decoder.
+ 	
+ 	Because of the small codesize of Castor, the decoder may
+ 	be the right one for e.g. firmware/HDL/ASIC developers.
+ 	
+ 	A C++ version of this decoder should be tested with the huffman decoding
+ 	approach of the reference decoder.
+ 		
+ 	Floor 0 decoding may be implemented, if neccessary.
+ 	According to
+ 	
+	http://www.mp3-tech.org/programmer/docs/embedded_vorbis_thesis.pdf
+	
+	is this floor type not used anymore, due to poor performance and high decoder
+ 	complexity compared to floor 1. Floor 0 was replaced in an early beta, but is still part
+ 	of the standard end hence needs to be supported by a compliant decoder.
+ 	
+ 	Since the current Ogg/Vorbis standard includes features that are not currently used,
+ 	some very unlikely to ever be used (e.g. window lengths of up to 8192 samples), and
+ 	others that are simply outdated (e.g. floor type 0), it is possible to remove support
+ 	for these features from the decoder while still being able to play back almost every
+ 	available Ogg/Vorbis stream. Of course the decoder can no longer be considered fully
+ 	compliant with the standard.
+ 	
+ 	Appreciations:
+ 	
+ 	Very thanks to Dr. m. m. Timothy B. Terriberry for reviewing
+ 	the huffman decoding.

Added: trunk/cronus/com/meviatronic/zeus/castor/VorbisDecoder.java
===================================================================
--- trunk/cronus/com/meviatronic/zeus/castor/VorbisDecoder.java	                        (rev 0)
+++ trunk/cronus/com/meviatronic/zeus/castor/VorbisDecoder.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,1045 @@
+/* Castor, a fast Vorbis decoder created by Michael Scheerer.
+ *
+ * Castor decoder (c) 2010 Michael Scheerer www.meviatronic.com
+ *
+ * Many thanks to
+ *   Monty <monty at xiph.org> and
+ *   The XIPHOPHORUS Company http://www.xiph.org/ .
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+   
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+package com.meviatronic.zeus.castor;
+
+import java.io.*;
+
+/**
+ * The <code>VorbisDecoder</code> class handles the main decoding of all vorbis formats.
+ *
+ * @author	Michael Scheerer
+ */
+public final class VorbisDecoder extends Output {
+	
+	private final static int FLOOR_MULTIPLIER[] = {
+		256, 128, 86, 64
+	};
+	
+	private final static float FLOOR1_INVERSE_DB_TABLE[] = {
+		1.0649863e-07F, 1.1341951e-07F, 1.2079015e-07F, 1.2863978e-07F,
+		1.3699951e-07F, 1.4590251e-07F, 1.5538408e-07F, 1.6548181e-07F,
+		1.7623575e-07F, 1.8768855e-07F, 1.9988561e-07F, 2.128753e-07F,
+		2.2670913e-07F, 2.4144197e-07F, 2.5713223e-07F, 2.7384213e-07F,
+		2.9163793e-07F, 3.1059021e-07F, 3.3077411e-07F, 3.5226968e-07F,
+		3.7516214e-07F, 3.9954229e-07F, 4.2550680e-07F, 4.5315863e-07F,
+		4.8260743e-07F, 5.1396998e-07F, 5.4737065e-07F, 5.8294187e-07F,
+		6.2082472e-07F, 6.6116941e-07F, 7.0413592e-07F, 7.4989464e-07F,
+		7.9862701e-07F, 8.5052630e-07F, 9.0579828e-07F, 9.6466216e-07F,
+		1.0273513e-06F, 1.0941144e-06F, 1.1652161e-06F, 1.2409384e-06F,
+		1.3215816e-06F, 1.4074654e-06F, 1.4989305e-06F, 1.5963394e-06F,
+		1.7000785e-06F, 1.8105592e-06F, 1.9282195e-06F, 2.0535261e-06F,
+		2.1869758e-06F, 2.3290978e-06F, 2.4804557e-06F, 2.6416497e-06F,
+		2.8133190e-06F, 2.9961443e-06F, 3.1908506e-06F, 3.3982101e-06F,
+		3.6190449e-06F, 3.8542308e-06F, 4.1047004e-06F, 4.3714470e-06F,
+		4.6555282e-06F, 4.9580707e-06F, 5.2802740e-06F, 5.6234160e-06F,
+		5.9888572e-06F, 6.3780469e-06F, 6.7925283e-06F, 7.2339451e-06F,
+		7.7040476e-06F, 8.2047000e-06F, 8.7378876e-06F, 9.3057248e-06F,
+		9.9104632e-06F, 1.0554501e-05F, 1.1240392e-05F, 1.1970856e-05F,
+		1.2748789e-05F, 1.3577278e-05F, 1.4459606e-05F, 1.5399272e-05F,
+		1.6400004e-05F, 1.7465768e-05F, 1.8600792e-05F, 1.9809576e-05F,
+		2.1096914e-05F, 2.2467911e-05F, 2.3928002e-05F, 2.5482978e-05F,
+		2.7139006e-05F, 2.8902651e-05F, 3.0780908e-05F, 3.2781225e-05F,
+		3.4911534e-05F, 3.7180282e-05F, 3.9596466e-05F, 4.2169667e-05F,
+		4.4910090e-05F, 4.7828601e-05F, 5.0936773e-05F, 5.4246931e-05F,
+		5.7772202e-05F, 6.1526565e-05F, 6.5524908e-05F, 6.9783085e-05F,
+		7.4317983e-05F, 7.9147585e-05F, 8.4291040e-05F, 8.9768747e-05F,
+		9.5602426e-05F, 0.00010181521F, 0.00010843174F, 0.00011547824F,
+		0.00012298267F, 0.00013097477F, 0.00013948625F, 0.00014855085F,
+		0.00015820453F, 0.00016848555F, 0.00017943469F, 0.00019109536F,
+		0.00020351382F, 0.00021673929F, 0.00023082423F, 0.00024582449F,
+		0.00026179955F, 0.00027881276F, 0.00029693158F, 0.00031622787F,
+		0.00033677814F, 0.00035866388F, 0.00038197188F, 0.00040679456F,
+		0.00043323036F, 0.00046138411F, 0.00049136745F, 0.00052329927F,
+		0.00055730621F, 0.00059352311F, 0.00063209358F, 0.00067317058F,
+		0.00071691700F, 0.00076350630F, 0.00081312324F, 0.00086596457F,
+		0.00092223983F, 0.00098217216F, 0.0010459992F, 0.0011139742F,
+		0.0011863665F, 0.0012634633F, 0.0013455702F, 0.0014330129F,
+		0.0015261382F, 0.0016253153F, 0.0017309374F, 0.0018434235F,
+		0.0019632195F, 0.0020908006F, 0.0022266726F, 0.0023713743F,
+		0.0025254795F, 0.0026895994F, 0.0028643847F, 0.0030505286F,
+		0.0032487691F, 0.0034598925F, 0.0036847358F, 0.0039241906F,
+		0.0041792066F, 0.0044507950F, 0.0047400328F, 0.0050480668F,
+		0.0053761186F, 0.0057254891F, 0.0060975636F, 0.0064938176F,
+		0.0069158225F, 0.0073652516F, 0.0078438871F, 0.0083536271F,
+		0.0088964928F, 0.009474637F, 0.010090352F, 0.010746080F,
+		0.011444421F, 0.012188144F, 0.012980198F, 0.013823725F,
+		0.014722068F, 0.015678791F, 0.016697687F, 0.017782797F,
+		0.018938423F, 0.020169149F, 0.021479854F, 0.022875735F,
+		0.024362330F, 0.025945531F, 0.027631618F, 0.029427276F,
+		0.031339626F, 0.033376252F, 0.035545228F, 0.037855157F,
+		0.040315199F, 0.042935108F, 0.045725273F, 0.048696758F,
+		0.051861348F, 0.055231591F, 0.058820850F, 0.062643361F,
+		0.066714279F, 0.071049749F, 0.075666962F, 0.080584227F,
+		0.085821044F, 0.091398179F, 0.097337747F, 0.10366330F,
+		0.11039993F, 0.11757434F, 0.12521498F, 0.13335215F,
+		0.14201813F, 0.15124727F, 0.16107617F, 0.17154380F,
+		0.18269168F, 0.19456402F, 0.20720788F, 0.22067342F,
+		0.23501402F, 0.25028656F, 0.26655159F, 0.28387361F,
+		0.30232132F, 0.32196786F, 0.34289114F, 0.36517414F,
+		0.38890521F, 0.41417847F, 0.44109412F, 0.46975890F,
+		0.50028648F, 0.53279791F, 0.56742212F, 0.60429640F,
+		0.64356699F, 0.68538959F, 0.72993007F, 0.77736504F,
+		0.82788260F, 0.88168307F, 0.9389798F, 1.F
+	};
+	
+	private Imdct[] imdct = {new Imdct(), new Imdct()};
+	private float[][] pcm;
+	private float[][] previousPcm;
+	private float[][] windowTable = new float[5][];
+	private float[] window;
+	private float[] zeroFloats;
+	private int blocksize0, blocksize1, n, previousN;
+	private int modeNumber;
+	private int modeNumberBits;
+	private int vorbisModeBlockflag;
+	private int previousVorbisModeBlockflag;
+	private int vorbisModeMapping;
+	private boolean[] noResidue;
+	private float[][] floor;
+	private float[][] residue;
+	private float[][] residueBundle;
+	private Imdct cimdct;
+	private int[][] lowNeighborOffsetTable;
+	private int[][] highNeighborOffsetTable;
+	private int[][] sortTable;
+	private int[][][] classificationTable;
+	private int[][][] classifications;
+	private int[] floor1Step2Flag;
+	private int[] floor1FinalY;
+	private int[] floor1Y;
+	
+	/**
+	 * Constructs an instance of <code>VorbisDecoder</code> with a
+	 * <code>AudioReader</code> object containing all necessary informations
+	 * about the media source.
+	 *
+	 * @param info     the <code>AudioReader</code> object containing all
+	 *      necessary informations about the media source
+	 */
+	public VorbisDecoder(AudioReader info) {
+		super(info);
+		int i;
+		int floor1MaximumValues = info.floor1MaximumValues;
+		int floor1XListLength = info.floor1XList.length;
+		
+		blocksize0 = info.blocksizes[0];
+		blocksize1 = info.blocksizes[1];
+		modeNumberBits = info.modeNumberBits;
+		floor = new float[channels][blocksize1 >>> 1];
+		residue = new float[channels][blocksize1 >>> 1];
+		residueBundle = new float[channels][];
+		
+		zeroFloats = new float[blocksize1];
+		
+		lowNeighborOffsetTable = new int[floor1XListLength][];
+		highNeighborOffsetTable = new int[floor1XListLength][];
+		sortTable = new int[floor1XListLength][];
+		
+		floor1Step2Flag = new int[floor1MaximumValues];
+		floor1FinalY = new int[floor1MaximumValues];
+		floor1Y = new int[floor1MaximumValues];
+		
+		for (i = 0; i < floor1XListLength; i++) {
+			initializeFloorDecoding(i);
+		}
+		
+		classifications = new int[channels][blocksize1 >>> 1][];
+		initializeClassification();
+		
+		pcm = new float[channels][blocksize1];
+		noResidue = new boolean[channels];
+		previousPcm = new float[channels][blocksize1];
+		
+		windowTable[0] = new float[blocksize0];
+		windowTable[1] = new float[blocksize1];
+		windowTable[2] = new float[blocksize1];
+		windowTable[3] = new float[blocksize1];
+		windowTable[4] = new float[blocksize1];
+		
+		imdct[0].initialize(blocksize0);
+		imdct[1].initialize(blocksize1);
+		
+		initializeWindowing(blocksize0, 0, 0, 0);
+		initializeWindowing(blocksize1, 1, 0, 0);
+		initializeWindowing(blocksize1, 1, 0, 1);
+		initializeWindowing(blocksize1, 1, 1, 0);
+		initializeWindowing(blocksize1, 1, 1, 1);
+	}
+	
+	void decode() throws IOException, EndOfMediaException, EndOfPacketException {
+		int i;
+		
+		if (get1() != 0) {
+			throw new InterruptedIOException("No audio packet");
+		}
+		
+		modeNumber = get(modeNumberBits);
+		vorbisModeBlockflag = info.vorbisModeBlockflag[modeNumber];
+		n = info.blocksizes[vorbisModeBlockflag];
+		previousN = info.blocksizes[previousVorbisModeBlockflag];
+		
+		for (i = 0; i < channels; i++) {
+			noResidue[i] = false;
+			System.arraycopy(zeroFloats, 0, residue[i], 0, n / 2);
+		}
+		
+		cimdct = imdct[vorbisModeBlockflag];
+		
+		setBlockSize(previousN + n >>> 2, n);
+		
+		int previousWindowFlag = 0, nextWindowFlag = 0;
+		
+		int index = 0;
+		
+		if (vorbisModeBlockflag == 1) {
+			previousWindowFlag = get1();
+			nextWindowFlag = get1();
+		}
+		
+		if (previousWindowFlag == 0 && nextWindowFlag == 1) {
+			index = 2;
+		} else if (previousWindowFlag == 1 && nextWindowFlag == 0) {
+			index = 3;
+		} else if (previousWindowFlag == 1 && nextWindowFlag == 1) {
+			index = 4;
+		} else if (vorbisModeBlockflag == 1) {
+			index = 1;
+		}
+	
+		window = windowTable[index];
+		
+		vorbisModeMapping = info.vorbisModeMapping[modeNumber];
+		
+		boolean noResidues = false;
+		
+		try {
+			for (i = 0; i < channels; i++) {
+				decodeFloor(i);
+			}
+		} catch (EndOfPacketException e) {
+			noResidues = true;
+		}
+		if (!noResidues) {
+			nonzeroVectorPropagate();
+			try {
+				if (decodeResidue()) {
+					noResidues = true;
+				}
+			} catch (EndOfPacketException e) {}
+			if (!noResidues) {
+				inverseCoupling();
+			}
+		}
+		if (noResidues) {
+			for (i = 0; i < channels; i++) {
+				float[] p = pcm[i];
+				System.arraycopy(zeroFloats, 0, p, 0, p.length);
+			}
+		} else {
+			for (i = 0; i < channels; i++) {
+				if (noResidue[i]) {
+					float[] p = pcm[i];
+					System.arraycopy(zeroFloats, 0, p, 0, p.length);
+					continue;
+				}
+				imdct(residue[i], pcm[i], window, floor[i]);
+			}
+		}
+		postProcessingPcm();
+	}
+	
+	private void decodeFloor(int channel) throws IOException, EndOfMediaException, EndOfPacketException {
+		int i, j;
+		int submapNumber = info.vorbisMappingMux[vorbisModeMapping][channel];
+		int floorNumber = info.vorbisMappingSubmapFloor[vorbisModeMapping][submapNumber];
+			
+		if (get1() == 0) {
+			noResidue[channel] = true;
+			return;
+		}
+			
+		int floor1Multiplier = info.floor1Multiplieres[floorNumber];
+		int range = FLOOR_MULTIPLIER[floor1Multiplier - 1];
+			
+		int[] floor1XList = info.floor1XList[floorNumber];
+		
+		int floor1Values = floor1XList.length;
+		
+		floor1Y[0] = get(ilog(range - 1));
+		floor1Y[1] = get(ilog(range - 1));
+			
+		int offset = 2;
+		int[] floor1PartitionClass = info.floor1PartitionClassList[floorNumber];
+		int floor1Partitions = floor1PartitionClass.length;
+		int[] floor1ClassDimensions = info.floor1ClassDimensions[floorNumber];
+		int[] floor1ClassMasterbooks = info.floor1ClassMasterbooks[floorNumber];
+		int[] floor1ClassSubclasses = info.floor1ClassSubclasses[floorNumber];
+		int[][] floor1SubclassBooks = info.floor1SubclassBooks[floorNumber];
+			
+		int pclass;
+		int cdim;
+		int cbits;
+		int csub;
+		int cval;
+		int book;
+		int cdimPlusOffset;
+			
+		for (i = 0; i < floor1Partitions; i++) {
+			pclass = floor1PartitionClass[i];
+			cdim  = floor1ClassDimensions[pclass];
+			cdimPlusOffset = cdim + offset;
+			cbits = floor1ClassSubclasses[pclass];
+			csub = (1 << cbits) - 1;
+			cval = 0;
+			if (cbits > 0) {
+				cval = getCodeWord(floor1ClassMasterbooks[pclass]);
+			}
+			for (j = offset; j < cdimPlusOffset; j++) {
+				book = floor1SubclassBooks[pclass][cval & csub];
+				cval >>>= cbits;
+				if (book >= 0) {
+					floor1Y[j] = getCodeWord(book);
+				} else {
+					floor1Y[j] = 0;
+				}
+			}
+			offset += cdim;
+		}
+		
+		floor1Step2Flag[0] = 1;
+		floor1Step2Flag[1] = 1;
+		floor1FinalY[0] = floor1Y[0];
+		floor1FinalY[1] = floor1Y[1];
+			
+		int lowNeighborOffset;
+		int highNeighborOffset;
+		int predicted;
+		int val;
+		int highroom;
+		int lowroom;
+		int room;
+			
+		int[] low = lowNeighborOffsetTable[floorNumber];
+		int[] high = highNeighborOffsetTable[floorNumber];
+		int[] sort = sortTable[floorNumber];
+
+		for (i = 2; i < floor1Values; i++) {
+			lowNeighborOffset = low[i];
+			highNeighborOffset = high[i];
+			predicted = renderPoint(floor1XList[lowNeighborOffset],
+									floor1FinalY[lowNeighborOffset],
+									floor1XList[highNeighborOffset],
+									floor1FinalY[highNeighborOffset],
+									floor1XList[i]);
+			
+			val = floor1Y[i];
+			highroom = range - predicted;
+			lowroom = predicted;
+				
+			room = (highroom < lowroom ? highroom : lowroom) << 1;
+
+			if (val != 0) {
+				floor1Step2Flag[lowNeighborOffset] = 1;
+				floor1Step2Flag[highNeighborOffset] = 1;
+				floor1Step2Flag[i] = 1;
+				if (val >= room) {
+					if (highroom > lowroom) {
+						floor1FinalY[i] = val - lowroom + predicted;
+					} else {
+						floor1FinalY[i] = predicted - val + highroom - 1;
+					}
+				} else {
+					if ((val & 0x1) == 1) {
+						floor1FinalY[i] = predicted - ((val + 1) >>> 1);
+					} else {
+						floor1FinalY[i] = predicted + (val >>> 1);
+					}
+				}
+			} else {
+				floor1Step2Flag[i] = 0;
+				floor1FinalY[i] = predicted;
+			}
+		}
+			
+		float[] flro = floor[channel];
+		int hx = 0;
+		int hy = 0;
+		int lx = 0;
+		int ly = floor1FinalY[0] * floor1Multiplier;
+		int actualSize = n / 2;
+
+		for (i = 1; i < floor1Values; i++) {
+			j = sort[i];
+			if (floor1Step2Flag[j] == 1) {
+				hy = floor1FinalY[j] * floor1Multiplier;
+				hx = floor1XList[j];
+				renderLine(lx, ly, hx, hy, flro);
+				lx = hx;
+				ly = hy;
+			}
+		}
+		if (hx < actualSize) {
+			renderLine(hx, hy, actualSize, hy, flro);
+		}
+	}
+	
+	private void nonzeroVectorPropagate() {
+		int[] magnitude = info.vorbisMappingMagnitude[vorbisModeMapping];
+		int[] angle = info.vorbisMappingAngle[vorbisModeMapping];
+		int m;
+		int a;
+		int i;
+		
+		for (i = 0; i < magnitude.length; i++) {
+			m = magnitude[i];
+			a = angle[i];
+			if (!noResidue[m] || !noResidue[a]) {
+				noResidue[m] = false;
+				noResidue[a] = false;
+			}
+		}
+	}
+
+	private boolean decodeResidue() throws IOException, EndOfMediaException, EndOfPacketException {
+		int i, j;
+		int[] vorbisMappingSubmapResidue = info.vorbisMappingSubmapResidue[vorbisModeMapping];
+		int[] vorbisMappingMux = info.vorbisMappingMux[vorbisModeMapping];
+		int ch;
+		int dch, dch2;
+		boolean doNotDecode;
+		int residueNumber;
+		int residueType;
+
+		for (i = 0; i < vorbisMappingSubmapResidue.length; i++) {
+			ch = dch = dch2 = 0;
+			residueNumber = vorbisMappingSubmapResidue[i];
+			residueType = info.vorbisResidueTypes[residueNumber];
+			
+			for (j = 0; j < channels; j++) {
+				if (vorbisMappingMux[j] == i) {
+					if (!(doNotDecode = noResidue[j]) || residueType == 2) {
+						residueBundle[dch++] = residue[j];
+						if (!doNotDecode) {
+							dch2++;
+						}
+					}
+					ch++;
+				}
+			}
+			if (dch2 == 0) {
+				return false;
+			}
+			if (decodeVectors(dch, residueNumber, residueType)) {
+				continue;
+			}
+		}
+		return false;
+	}
+				
+	private boolean decodeVectors(int dch, int residueNumber, int residueType) throws IOException, EndOfMediaException, EndOfPacketException {
+		int i, j, k, l, pass, entryTemp, end;
+		int residueBegin = info.residueBegin[residueNumber];
+		int residueEnd = info.residueEnd[residueNumber];
+		int residuePartitionSize = info.residuePartitionSize[residueNumber];
+		int residueClassifications = info.residueClassifications[residueNumber];
+		int residueClassbook = info.residueClassbooks[residueNumber];
+		int maximumPasses = info.residueMaximumPasses[residueNumber];
+		int[] residueCascade = info.residueCascade[residueNumber];
+		int[][] residueBooks = info.residueBooks[residueNumber];
+		int actualSize = n / 2;
+		int vectors = dch;
+
+		if (residueType == 2) {
+			actualSize *= dch;
+		}
+		
+		int limitResidueBegin = residueBegin < actualSize ? residueBegin : actualSize;
+		int limitResidueEnd = residueEnd < actualSize ? residueEnd : actualSize;
+		int codebookDimensions[] = info.codebookDimensions;
+		int codebookDimension;
+		int classwordsPerCodeword = codebookDimensions[residueClassbook];
+		float[][] vqLookupTable = info.valueVector;
+		float[] vqLookup;
+		float[] out;
+
+		int nToRead = limitResidueEnd - limitResidueBegin;
+		int partitionsToRead = nToRead / residuePartitionSize;
+		
+		if (nToRead == 0) {
+			return true;
+		}
+
+		int partitionCount;
+		int codewordCount;
+		int temp;
+		int vqclass;
+		int vqbook;
+		
+		int[][] classificationTablePerResidue = classificationTable[residueNumber];
+		
+		for (pass = 0; pass < maximumPasses; pass++) {
+			partitionCount = 0;
+			codewordCount = 0;
+			
+			if (residueType == 0) {
+				while (partitionCount < partitionsToRead) {
+					if (pass == 0) {
+						for (j = 0; j < vectors; j++) {
+							temp = getCodeWord(residueClassbook);
+							classifications[j][codewordCount] = classificationTablePerResidue[temp];
+						}
+					}
+					for (i = 0; i < classwordsPerCodeword && partitionCount < partitionsToRead; i++) {
+						k = end = limitResidueBegin + partitionCount * residuePartitionSize;
+						for (j = 0; j < vectors; j++) {
+							vqclass = classifications[j][codewordCount][i];
+							out = residueBundle[j];
+							if ((residueCascade[vqclass] & 1 << pass) != 0) {
+								vqbook = residueBooks[vqclass][pass];
+								vqLookup = vqLookupTable[vqbook];
+							
+								codebookDimension = codebookDimensions[vqbook];
+								if(bookIsUnused(vqbook)) {
+									continue;
+								}
+								int step = residuePartitionSize / codebookDimension;
+								end += step;
+
+								for (; k < end; k++) {
+									entryTemp = getCodeWord(vqbook) * codebookDimension;
+									for (l = 0; l < codebookDimension; l++) {
+										out[k + l * step] += vqLookup[entryTemp + l];
+									}
+								}
+							}
+						}
+						partitionCount++;
+					}
+					codewordCount++;
+				}
+			} else if (residueType == 1) {
+				while (partitionCount < partitionsToRead) {
+					if (pass == 0) {
+						for (j = 0; j < vectors; j++) {
+							temp = getCodeWord(residueClassbook);
+							classifications[j][codewordCount] = classificationTablePerResidue[temp];
+						}
+					}
+					for (i = 0; i < classwordsPerCodeword && partitionCount < partitionsToRead; i++) {
+						k = end = limitResidueBegin + partitionCount * residuePartitionSize;
+							
+						for (j = 0; j < vectors; j++) {
+							vqclass = classifications[j][codewordCount][i];
+							out = residueBundle[j];
+							if ((residueCascade[vqclass] & 1 << pass) != 0) {
+								vqbook = residueBooks[vqclass][pass];
+								vqLookup = vqLookupTable[vqbook];
+							
+								codebookDimension = codebookDimensions[vqbook];
+								if(bookIsUnused(vqbook)) {
+									continue;
+								}
+								end += residuePartitionSize;
+
+								do {
+									entryTemp = getCodeWord(vqbook) * codebookDimension;
+									for (l = 0; l < codebookDimension; l++, k++) {
+										out[k] += vqLookup[entryTemp + l];
+									}
+								} while (k < end);
+							}
+						}
+						partitionCount++;
+					}
+					codewordCount++;
+				}
+			} else {
+				while (partitionCount < partitionsToRead) {
+					if (pass == 0) {
+						temp = getCodeWord(residueClassbook);
+						classifications[0][codewordCount] = classificationTablePerResidue[temp];
+					}
+					for (i = 0; i < classwordsPerCodeword && partitionCount < partitionsToRead; i++) {
+						k = end = limitResidueBegin + partitionCount * residuePartitionSize;
+						vqclass = classifications[0][codewordCount][i];
+						if ((residueCascade[vqclass] & 1 << pass) != 0) {
+							vqbook = residueBooks[vqclass][pass];
+							vqLookup = vqLookupTable[vqbook];
+							
+							codebookDimension = codebookDimensions[vqbook];
+							if(bookIsUnused(vqbook)) {
+								continue;
+							}
+							end += residuePartitionSize;
+							
+							if (codebookDimension == 2) {
+								do {
+									entryTemp = getCodeWord(vqbook) * codebookDimension;
+									residueBundle[k % dch][k / dch] += vqLookup[entryTemp];
+									k++;
+									residueBundle[k % dch][k / dch] += vqLookup[entryTemp + 1];
+									k++;
+								} while (k < end);
+							} else if (codebookDimension == 4) {
+								do {
+									entryTemp = getCodeWord(vqbook) * codebookDimension;
+									residueBundle[k % dch][k / dch] += vqLookup[entryTemp];
+									k++;
+									residueBundle[k % dch][k / dch] += vqLookup[entryTemp + 1];
+									k++;
+									residueBundle[k % dch][k / dch] += vqLookup[entryTemp + 2];
+									k++;
+									residueBundle[k % dch][k / dch] += vqLookup[entryTemp + 3];
+									k++;
+								} while (k < end);
+							} else {
+								do {
+									entryTemp = getCodeWord(vqbook) * codebookDimension;
+									for (l = 0; l < codebookDimension; l++, k++) {
+										residueBundle[k % dch][k / dch] += vqLookup[entryTemp + l];
+									}
+								} while (k < end);
+							}
+						}
+						partitionCount++;
+					}
+					codewordCount++;
+				}
+			}
+		}
+		return false;
+	}
+
+	private void inverseCoupling() {
+		int[] magnitude = info.vorbisMappingMagnitude[vorbisModeMapping];
+		int[] angle = info.vorbisMappingAngle[vorbisModeMapping];
+		float[] magnitudeVector;
+		float[] angleVector;
+		float m, a, newM, newA;
+		
+		for (int i = magnitude.length - 1; i >= 0; i--) {
+			magnitudeVector = residue[magnitude[i]];
+			angleVector = residue[angle[i]];
+			
+			for (int j = 0; j < magnitudeVector.length; j++) {
+				m = magnitudeVector[j];
+				a = angleVector[j];
+				if (m > 0) {
+					if (a > 0) {
+						newM = m;
+						newA = m - a;
+					} else {
+						newA = m;
+						newM = m + a;
+					}
+				} else {
+					if (a > 0) {
+						newM = m;
+						newA = m + a;
+					} else {
+						newA = m;
+						newM = m - a;
+					}
+				}
+				magnitudeVector[j] = newM;
+				angleVector[j] = newA;
+			}
+		}
+	}
+	
+	private int lowNeighbor(int[] v, int x) {
+		int nMax = 0;
+		int treshold = -1;
+		int vn, vx;
+		
+		for (int n = x - 1; n >= 0; n--) {
+			vn = v[n];
+			vx = v[x];
+			
+			if (vn > treshold && vn < vx) {
+				treshold = vn;
+				nMax = n;
+			}
+		}
+		return nMax;
+	}
+	
+	private int highNeighbor(int[] v, int x) {
+		int nMin = 0;
+		int treshold = Integer.MAX_VALUE;
+		int vn, vx;
+		
+		for (int n = 0; n < x; n++) {
+			vn = v[n];
+			vx = v[x];
+			
+			if (vn < treshold && vn > vx) {
+				treshold = vn;
+				nMin = n;
+			}
+		}
+		return nMin;
+	}
+	
+	private static int renderPoint(int x0, int y0, int x1, int y1, int x) {
+		int dy = y1 - y0;
+		int adx = x1 - x0;
+		int ady = dy;
+		
+		if (ady < 0) {
+			ady = -ady;
+		}
+		
+		int err = ady * (x - x0);
+		int off = err / adx;
+		
+		if (dy < 0) {
+			return y0 - off;
+		}
+		return y0 + off;
+	}
+	
+	private void renderLine(int x0, int y0, int x1, int y1, float[] v) {
+		int dy = y1 - y0;
+		int adx = x1 - x0;
+		int ady = dy;
+		
+		if (ady < 0) {
+			ady = -ady;
+		}
+		
+		int x = x0;
+		int y = y0;
+		int err = -adx;
+		int sy = dy < 0 ? - 1 : + 1;
+		int base = dy / adx;
+		
+		v[x] = FLOOR1_INVERSE_DB_TABLE[y];
+			
+		if (base == 0) {
+			
+			if (ady << 1 <= adx) {
+				int x1MinusOne = x1 - 1;
+				int adyMinusAdx = ady - adx;
+					
+				while (++x < x1MinusOne) {
+					err += ady;
+			
+					if (err >= 0) {
+						err += adyMinusAdx;
+						y += sy;
+						v[x++] = FLOOR1_INVERSE_DB_TABLE[y];
+					}
+				
+					v[x] = FLOOR1_INVERSE_DB_TABLE[y];
+				}
+				if (x < x1) {
+					if (err + ady >= 0) {
+						y += sy;
+					}
+					v[x] = FLOOR1_INVERSE_DB_TABLE[y];
+				}
+			} else {
+				while (++x < x1) {
+					err += ady;
+			
+					if (err >= 0) {
+						err -= adx;
+						y += sy;
+					}
+				
+					v[x] = FLOOR1_INVERSE_DB_TABLE[y];
+				}
+			}
+		 } else {
+			int abase = base;
+		
+			if (abase < 0) {
+				abase = -abase;
+			}
+
+			ady -= abase * adx;
+			
+			while (++x < x1) {
+				err += ady;
+			
+				if (err >= 0) {
+					err -= adx;
+					y += sy;
+				}
+				
+				y += base;
+				
+				v[x] = FLOOR1_INVERSE_DB_TABLE[y];
+			}
+		}
+	}
+	
+	private void imdct(float[] in, float[] out, float[] window, float[] floor) {
+		cimdct.decode(in, out, window, floor);
+	}
+
+	private void postProcessingPcm() {
+		int i = 0;
+		int j, k = 0;
+		int nHalf = n >>> 1;
+		int previousNhalf = previousN >>> 1;
+		int begin = 0;
+		int middle = nHalf + previousNhalf >>> 1;
+		int end = middle;
+		int kOffset = 0;
+		float f;
+		short w;
+		byte b2;
+		byte b1;
+		int p;
+		
+		if (previousVorbisModeBlockflag == 1 && vorbisModeBlockflag == 0) {
+			begin = previousNhalf - nHalf >>> 1;
+		} else if (previousVorbisModeBlockflag == 0 && vorbisModeBlockflag == 1) {
+			middle = previousNhalf;
+			kOffset = nHalf - previousNhalf >>> 1;
+		}
+		for (; i < begin; i++) {
+			for (j = 0; j < channels; j++) {
+				setBuffer(previousPcm[j][i], j);
+			}
+		}
+		for (; i < middle; i++, k++) {
+			for (j = 0; j < channels; j++) {
+				setBuffer(previousPcm[j][i] + pcm[j][kOffset + k], j);
+			}
+		}
+		for (; i < end; i++, k++) {
+			for (j = 0; j < channels; j++) {
+				setBuffer(pcm[j][kOffset + k], j);
+			}
+		}
+		for (j = 0; j < channels; j++) {
+			System.arraycopy(pcm[j], nHalf, previousPcm[j], 0, nHalf);
+		}
+		previousVorbisModeBlockflag = vorbisModeBlockflag;
+	}
+
+	private void initializeWindowing(int n, int vorbisModeBlockflag, int previousWindowFlag, int nextWindowFlag) {
+		int index = 0;
+		int windowCenter = n / 2;
+		int rightWindowStart, rightWindowEnd, leftWindowStart, leftWindowEnd, leftN, rightN;
+		
+		if (vorbisModeBlockflag == 1 && previousWindowFlag == 0) {
+			leftN = blocksize0 / 2;
+			leftWindowStart = n / 4 - leftN / 2;
+			leftWindowEnd = n / 4 + leftN / 2;
+		} else {
+			leftWindowStart = 0;
+			leftN = leftWindowEnd = windowCenter;
+		}
+			
+		if (vorbisModeBlockflag == 1 && nextWindowFlag == 0) {
+			rightN = blocksize0 / 2;
+			rightWindowStart = n * 3 / 4 - rightN / 2;
+			rightWindowEnd = n * 3 / 4 + rightN / 2;
+		} else {
+			rightN = rightWindowStart = windowCenter;
+			rightWindowEnd = n;
+		}
+		
+		if (previousWindowFlag == 0 && nextWindowFlag == 1) {
+			index = 2;
+		} else if (previousWindowFlag == 1 && nextWindowFlag == 0) {
+			index = 3;
+		} else if (previousWindowFlag == 1 && nextWindowFlag == 1) {
+			index = 4;
+		} else if (vorbisModeBlockflag == 1) {
+			index = 1;
+		}
+
+		int i = leftWindowStart;
+		
+		float[] windowTablePointer = windowTable[index];
+
+		for (; i < leftWindowEnd; i++) {
+			windowTablePointer[i] = (float) Math.sin(0.5 * Math.PI * Math.pow(Math.sin((i - leftWindowStart + 0.5) / leftN * 0.5 * Math.PI), 2));
+		}
+		for (; i < rightWindowStart; i++) {
+			windowTablePointer[i] = 1;
+		}
+		for (; i < rightWindowEnd; i++) {
+			windowTablePointer[i] = (float) Math.sin(0.5 * Math.PI * Math.pow(Math.sin((rightN + i - rightWindowStart + 0.5) / rightN * 0.5 * Math.PI), 2));
+		}
+	}
+	
+	private void initializeFloorDecoding(int floorNumber) {
+		int i, j;
+		int[] floor1XList = info.floor1XList[floorNumber];
+		int floor1Values = floor1XList.length;
+		
+		int[] low = lowNeighborOffsetTable[floorNumber] = new int[floor1Values];
+		int[] high = highNeighborOffsetTable[floorNumber] = new int[floor1Values];
+		int[] sort = sortTable[floorNumber] = new int[floor1Values];
+		int buffer;
+
+		for (i = 1; i < floor1Values; i++) {
+			sort[i] = i;
+		}
+		for (i = 1; i < floor1Values; i++) {
+			for (j = i + 1; j < floor1Values && i < floor1Values - 1; j++) {
+				if (floor1XList[sort[i]] > floor1XList[sort[j]]) {
+					buffer = sort[i];
+					sort[i] = sort[j];
+					sort[j] = buffer;
+				}
+			}
+			low[i] = lowNeighbor(floor1XList, i);
+			high[i] = highNeighbor(floor1XList, i);
+		}
+	}
+	
+	private void initializeClassification() {
+		int i, j, temp;
+
+		classificationTable = new int[info.residueClassifications.length][][];
+		int[][] classificationTableOuterPointer;
+		int[] classificationTableInnerPointer;
+
+		for (i = 0; i < info.residueClassifications.length; i++) {
+			int residueClassifications = info.residueClassifications[i];
+			int residueClassbooks = info.residueClassbooks[i];
+			int classwordsPerCodeword = info.codebookDimensions[residueClassbooks];
+			int maxNumber = (int) Math.pow(residueClassifications, classwordsPerCodeword);
+			int workingDigit = 0;
+			int tempBuffer;
+			int classification;
+			classificationTableOuterPointer = classificationTable[i] = new int[maxNumber][];
+			
+			for(temp = 0; temp < maxNumber; temp++) {
+				classificationTableInnerPointer = classificationTableOuterPointer[temp] = new int[classwordsPerCodeword];
+      			tempBuffer = temp;
+      			workingDigit = maxNumber / residueClassifications;
+				
+      			for(j = 0; j < classwordsPerCodeword; j++){
+					classification = tempBuffer / workingDigit;
+					tempBuffer -= classification * workingDigit;
+					workingDigit /= residueClassifications;
+					classificationTableInnerPointer[j] = classification;
+				}
+			}
+		}
+	}
+
+	/**
+	 * Frees all system resources, which are bounded to this object.
+	 */
+	public void close() {
+		super.close();
+		int i, j;
+		
+		if (pcm != null) {
+			for (i = 0; i < pcm.length; i++) {
+				pcm[i] = null;
+			}
+		}
+		pcm = null;
+		if (previousPcm != null) {
+			for (i = 0; i < previousPcm.length; i++) {
+				previousPcm[i] = null;
+			}
+		}
+		previousPcm = null;
+		
+		if (windowTable != null) {
+			for (i = 0; i < windowTable.length; i++) {
+				windowTable[i] = null;
+			}
+		}
+		windowTable = null;
+		
+		int[][] classificationTablePointer;
+		
+		if (classificationTable != null) {
+			for (i = 0; i < classificationTable.length; i++) {
+				classificationTablePointer = classificationTable[i];
+				for (j = 0; j < classificationTablePointer.length; j++) {
+					classificationTablePointer[j] = null;
+				}
+				classificationTablePointer = null;
+			}
+		}
+		classificationTable = null;
+		if (classifications != null) {
+			for (i = 0; i < classifications.length; i++) {
+				classifications[i] = null;
+			}
+		}
+		classifications = null;
+		if (floor != null) {
+			for (i = 0; i < floor.length; i++) {
+				floor[i] = null;
+			}
+		}
+		floor = null;
+		residueBundle = null;
+		
+		if (residue != null) {
+			for (i = 0; i < residue.length; i++) {
+				residue[i] = null;
+			}
+		}
+		residue = null;
+		
+		if (lowNeighborOffsetTable != null) {
+			for (i = 0; i < lowNeighborOffsetTable.length; i++) {
+				lowNeighborOffsetTable[i] = null;
+			}
+		}
+		lowNeighborOffsetTable = null;
+		
+		if (highNeighborOffsetTable != null) {
+			for (i = 0; i < highNeighborOffsetTable.length; i++) {
+				highNeighborOffsetTable[i] = null;
+			}
+		}
+		highNeighborOffsetTable = null;
+
+		if (sortTable != null) {
+			for (i = 0; i < sortTable.length; i++) {
+				sortTable[i] = null;
+			}
+		}
+		sortTable = null;
+		floor1Step2Flag = null;
+		floor1FinalY = null;
+		floor1Y = null;
+		noResidue = null;
+		zeroFloats = null;
+		if (imdct != null) {
+			imdct[0].close();
+			imdct[1].close();
+			imdct[0] = null;
+			imdct[1] = null;
+		}
+		imdct = null;
+	}
+}

Added: trunk/cronus/com/meviatronic/zeus/helen/BaseTag.java
===================================================================
--- trunk/cronus/com/meviatronic/zeus/helen/BaseTag.java	                        (rev 0)
+++ trunk/cronus/com/meviatronic/zeus/helen/BaseTag.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,53 @@
+/* Helena, a Comment decoder created by Michael Scheerer.
+ *
+ * Helena decoder (c) 2010 Michael Scheerer www.meviatronic.com
+ *
+ * Many thanks to
+ *   Monty <monty at xiph.org> and
+ *   The XIPHOPHORUS Company http://www.xiph.org/ .
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+   
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+package com.meviatronic.zeus.helen;
+
+import java.util.*;
+
+/**
+ * The <code>BaseTagContent</code> class contains the cloned information related to an Ogg Tag and
+ * is designed to be as flexible as possible to
+ * reduce the number of cases where information has to be returned as binary
+ * when it is rather more structured. <p>
+ *
+ * It provides storage for - a type (e.g. a MIME-type or a language, Text) - a
+ * subtype (text or binary) - a description (text) - the content (text or
+ * binary).
+ *
+ * @author    Michael Scheerer
+ */
+public final class BaseTag extends Hashtable {
+		
+	/**
+	 * Returns a <code>Hashtable</code> representation of this <code>Information</code> object.
+	 *
+	 * @return     a <code>Hashtable</code> representation of this <code>Information</code> object
+	 */
+	public Hashtable getHashtable() {
+		return (Hashtable) this.clone();
+	}
+}
+
+

Added: trunk/cronus/com/meviatronic/zeus/helen/Converter.java
===================================================================
--- trunk/cronus/com/meviatronic/zeus/helen/Converter.java	                        (rev 0)
+++ trunk/cronus/com/meviatronic/zeus/helen/Converter.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,77 @@
+/* Helena, a Comment decoder created by Michael Scheerer.
+ *
+ * Helena decoder (c) 2010 Michael Scheerer www.meviatronic.com
+ *
+ * Many thanks to
+ *   Monty <monty at xiph.org> and
+ *   The XIPHOPHORUS Company http://www.xiph.org/ .
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+   
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+package com.meviatronic.zeus.helen;
+
+import java.io.*;
+
+/**
+ * The <code>Converter</code> class can be used to transform UTF-8 data to characters.
+ *
+ * @author	Michael Scheerer
+ */
+final class Converter {
+
+    String convert(byte b[]) {
+    	return convert(b, 0, b.length);
+    }
+    
+    String convert(byte b[], int i, int j) {
+		int charIndex = 0, k, l, m, n;
+		
+		char onec = 0;
+		
+		StringBuffer buffer = new StringBuffer();
+		
+		for (int byteIndex = i; byteIndex < j;) {
+        	
+            byte oneb = b[byteIndex++];
+            
+            if (oneb >= 0) {
+				onec = (char) oneb;
+			} else {
+				k = oneb & 0xFF;
+				
+				if ((k >>> 5 & 0x7) == 6 && byteIndex < j) {
+					l = b[byteIndex++];
+					onec = (char)(k << 6 & 0x1F | l & 0x3F);
+				} else if ((k >>> 4 & 0xF) == 14 && byteIndex < j - 1) {
+					l = b[byteIndex++];
+					m = b[byteIndex++];
+					onec = (char)(k << 12 & 0xF | l << 6 & 0x3F | m & 0x3F);
+				} else if (byteIndex < j - 2) {
+					l = b[byteIndex++];
+					m = b[byteIndex++];
+					n = b[byteIndex++];
+					onec = (char)(k << 18 & 0x7 | l << 12 & 0x3F | m << 6 & 0x3F | n & 0x3F);
+				} else {
+					onec = ' ';
+				}
+			}
+			buffer.append(onec);
+		}
+		return buffer.toString();
+    }
+}
+

Added: trunk/cronus/com/meviatronic/zeus/helen/OggTag.java
===================================================================
--- trunk/cronus/com/meviatronic/zeus/helen/OggTag.java	                        (rev 0)
+++ trunk/cronus/com/meviatronic/zeus/helen/OggTag.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,336 @@
+/* Helena, a Comment decoder created by Michael Scheerer.
+ *
+ * Helena decoder (c) 2010 Michael Scheerer www.meviatronic.com
+ *
+ * Many thanks to
+ *   Monty <monty at xiph.org> and
+ *   The XIPHOPHORUS Company http://www.xiph.org/ .
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+   
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+package com.meviatronic.zeus.helen;
+
+import java.io.*;
+import java.util.*;
+
+import com.meviatronic.zeus.castor.*;
+import com.meviatronic.zeus.pollux.*;
+
+/**
+ * The <code>OggTag</code> class is the main class of this Ogg Comment system.
+ * <p>
+ * Note that this OggTag system requires an OggTag tag
+ * starting at the first byte of the inputstream,
+ * although the OggTag specification doesn't demand this.
+ *
+ * @author    Michael Scheerer
+ */
+public final class OggTag extends Tag {
+
+	/**
+	 * String frameContent keys
+	 */
+	public final static String
+		S_LOCATION = "String location", S_CONTACT = "String contact", S_DESCRIPTION = "String description", S_ORGANIZATION = "String organization",
+		S_VERSION = "String version", S_VENDOR = "String vendor", S_LICENSE = "String license", S_DIRECTOR = "String director", S_PRODUCER = "String producer", S_ACTOR = "String actor",
+		S_TAG = "String tag";
+	
+	private boolean framingBit;
+	private Hashtable frames;
+	private byte[] data;
+	private String vendorString;
+	private int i, j;
+	private int userCommentListLength;
+	private Converter con = new Converter();
+	private byte[] b;
+	private AudioReader reader;
+
+	/**
+	 * Provides access to Ogg tag.
+	 *
+	 * @param reader            AudioReader to read from
+	 * @param framingBit        if an framing bit occurs
+	 * @exception IOException   if an I/O errors occur
+	 */
+	public OggTag(AudioReader reader, boolean framingBit) throws IOException {
+		super();
+		this.framingBit = framingBit;
+		this.reader = reader;
+	}
+	
+	private void buildKeyMapTable() {
+		//text frameContent
+		put(S_ARTIST, "ARTIST");
+		put(S_ALBUM, "ALBUM");
+		put(S_ENCODER, "ENCODER");
+		put(S_TRACK, "TRACKNUMBER");
+		put(S_YEAR, "YEAR");
+		put(S_LOCATION, "LOCATION");
+		put(S_PERFORMER, "PERFORMER");
+		put(S_COPYRIGHT, "COPYRIGHT");
+		put(S_LICENSE, "LICENSE");
+		put(S_DATE, "DATE");
+		put(S_GENRE, "GENRE");
+		put(S_TITLE, "TITLE");
+		put(S_ISRC, "ISRC");
+		put(S_VERSION, "VERSION");
+		put(S_ORGANIZATION, "ORGANIZATION");
+		put(S_CONTACT, "CONTACT");
+		put(S_DESCRIPTION, "DESCRIPTION");
+	}
+
+	/**
+	 * Returns the value to which the specified key is mapped in this hashtable.
+	 * The values are either control flags or informations stored in the tag.
+	 *
+	 * @param key                       the hashtable key
+	 * @return                          the value to which the key is mapped in
+	 *      this hashtable; null if the key is not mapped to any value in this
+	 *      hashtable
+	 */
+	public Object get(Object key) {
+		Object ob = super.get(key);
+
+		if (key.equals(S_TAG_FORMAT) || key.equals(B_TAG) || key.equals(S_VENDOR)) {
+			return ob;
+		}
+		return frames.get(ob);
+	}
+	
+	/**
+	 * Obtains a String describing all information keys and values. With the hashkey
+	 * of a specific information it is possible to obtain the information value.
+	 *
+	 * @return     a String representation all informations
+	 */
+	public String toString() {
+		StringBuffer buff = new StringBuffer();
+		int counter = 0, counter1 = 0;
+		Object key;
+		Object value;
+		Enumeration ekeys1 = keys();
+		if (!ekeys1.hasMoreElements()) {
+			buff.append("{}");
+			return buff.toString();
+		}
+		for (key = ekeys1.nextElement(); ekeys1.hasMoreElements(); key = ekeys1.nextElement()) {
+			value = get(key);
+			if (value != null && !key.equals(S_VENDOR)) {
+				counter++;
+			}
+		}
+		Enumeration ekeys = keys();
+		buff.append("{");
+		buff.append(S_VENDOR + "=");
+		if (counter == 0) {
+			buff.append(get(S_VENDOR));
+		} else {
+			buff.append(get(S_VENDOR) + ", ");
+		}
+		for (key = ekeys.nextElement(); ekeys.hasMoreElements(); key = ekeys.nextElement()) {
+			value = get(key);
+			if (value != null && !key.equals(S_VENDOR)) {
+				buff.append(key + "=");
+				buff.append(value);
+				counter1++;
+				if (counter1 < counter) {
+					buff.append(", ");
+				}
+			}
+		}
+		buff.append("}");
+		return buff.toString();
+	}
+	
+    /**
+     * Creates a shallow copy of this hashtable. The keys and values
+     * themselves are cloned.
+     * This is a relatively expensive operation.
+	 *
+	 * @return     a clone of this instance
+	 */
+	public Object clone() {
+		BaseTag hash = new BaseTag();
+		
+		int i = size();
+		
+		if (i == 0) {
+			return hash;
+		}
+
+		Enumeration ekeys = keys();
+		Enumeration evalues = elements();
+		Object key, value;
+		for (int j = 0; j < i; j++) {
+			key = ekeys.nextElement();
+			value = get(key);
+			if (value != null) {
+				hash.put(key, value);
+			}
+		}
+		return hash;
+	}
+	
+	/*
+	 * Decodes the comment header packet
+	 * @exception IOException           if an I/O errors occur
+	 * @exception EndOfPacketExeption   if an end of packet occur
+	 */
+	public void decode() throws IOException, EndOfPacketException {
+		try {
+			readHeader(reader);
+			buildKeyMapTable();
+			put(S_VENDOR, new String(vendorString));
+			loadFrames(reader, framingBit);
+		} catch (TagException e) {
+			throw new IOException("Tag error");
+		}
+	}
+
+	/**
+	 * Frees all system resources, which are bounded to this object.
+	 */
+	public void close() {
+		con = null;
+		data = null;
+		b = null;
+		vendorString = null;
+		
+		if (frames != null) {
+			frames.clear();
+		}
+		frames = null;
+		super.close();
+	}
+	 
+	private void readHeader(AudioReader reader) throws TagException, IOException, EndOfPacketException {
+		int vendorLength = getInt();
+		
+		if(vendorLength < 0) {
+			throw new TagException("Negative String Size");
+		}
+
+		data = new byte[vendorLength];
+		
+		for (int i = 0; i < vendorLength; i++) {
+			data[i] = (byte) getByte();
+		}
+
+		vendorString = new String(data);
+		
+		userCommentListLength = getInt();
+		
+		if(userCommentListLength < 0) {
+			throw new TagException("Negative Comment List Size");
+		}
+		
+		frames = new Hashtable(userCommentListLength);
+	}
+
+	private void loadFrames(AudioReader reader, boolean framingBit) throws IOException, TagException, EndOfPacketException {
+		int length = 0;
+		
+		String key;
+		
+		String value;
+		
+		int i, j, k;
+		
+		String keyBuffer = "";
+		
+		String valueBuffer = "";
+		
+		for (i = 0; i < userCommentListLength; i++) {
+			length = getInt();
+			
+			if (length < 0) {
+				throw new TagException("Negative String Size");
+			}
+
+			data = new byte[length];
+			
+			for (j = 0; j < length; j++) {
+				data[j] = (byte) getByte();
+			}
+			
+			k = 0;
+			
+			for (; k < data.length; k++) {
+				if (data[k] == '=') {
+					break;
+				}
+			}
+			key = new String(data, 0, k).toUpperCase();
+			value = con.convert(data, k + 1, data.length);
+			
+			if (key.equals(keyBuffer)) {
+				value = valueBuffer+", "+value;
+			}
+			
+			keyBuffer = new String(key);
+			valueBuffer = new String(value);
+			frames.put(key, value);
+		}
+		if (framingBit) {
+			if (getLittleEndian1() == 0) {
+				throw new TagException("No Ogg Tag");
+			}
+		}
+	}
+	
+	/**
+	 * Returns a single bit.
+	 *
+	 * @return                               the integer value of one bit
+	 * @exception IOException                if one bit can't be retrieved
+	 * @exception EndOfPacketExeption        if an end of packet occur
+	 */
+	public int getLittleEndian1() throws IOException, EndOfPacketException {
+		return reader.getLittleEndian1();
+	}
+
+	/**
+	 * Returns a single byte.
+	 *
+	 * @return                               the integer value of one bit
+	 * @exception IOException                if one bit can't be retrieved
+	 * @exception EndOfPacketExeption        if an end of packet occur
+	 */
+	public int getByte() throws IOException, EndOfPacketException {
+		if (reader instanceof VideoReader) {
+			return ((VideoReader) reader).get(8);
+		}
+		return reader.getLittleEndian(8);
+	}
+	
+	/**
+	 * Returns an int
+	 *
+	 * @return                               the integer value
+	 * @param i                              the length in bits
+	 * @exception IOException                if the bits can't be retrieved
+	 * @exception EndOfPacketExeption        if an end of packet occur
+	 */
+	public int getInt() throws IOException, EndOfPacketException {
+		if (reader instanceof VideoReader) {
+			return ((VideoReader) reader).get(8) | ((VideoReader) reader).get(8) << 8 | ((VideoReader) reader).get(8) << 16 | ((VideoReader) reader).get(8) << 24;
+		}
+		return reader.getLittleEndian(32);
+	}
+}
+
+

Added: trunk/cronus/com/meviatronic/zeus/helen/README.txt
===================================================================
--- trunk/cronus/com/meviatronic/zeus/helen/README.txt	                        (rev 0)
+++ trunk/cronus/com/meviatronic/zeus/helen/README.txt	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,23 @@
+	Helen, a Comment decoder created by Michael Scheerer.
+	Helen decoder (c) 2009 Michael Scheerer www.meviatronic.com
+
+	Many thanks to
+	Monty <monty at xiph.org> and
+	The XIPHOPHORUS Company http://www.xiph.org/ .
+	
+	Helen's UTF-8 decoding based on:
+	Self developed algorithm, therefore there's no
+	reference.
+	
+	Helen's tag information processing system is adapted from the
+	media information processing system of the Lightweight Java Media Framework
+	(www.ljmf.org).
+	
+	Summary:
+	
+	Advantages of Helen over the current reference decoder:
+	- the actual most advanced Comment decoder.
+	
+	Todo:
+ 	
+ 	Nothing to do.

Added: trunk/cronus/com/meviatronic/zeus/helen/Tag.java
===================================================================
--- trunk/cronus/com/meviatronic/zeus/helen/Tag.java	                        (rev 0)
+++ trunk/cronus/com/meviatronic/zeus/helen/Tag.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,196 @@
+/* Helena, a Comment decoder created by Michael Scheerer.
+ *
+ * Helena decoder (c) 2010 Michael Scheerer www.meviatronic.com
+ *
+ * Many thanks to
+ *   Monty <monty at xiph.org> and
+ *   The XIPHOPHORUS Company http://www.xiph.org/ .
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+   
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+package com.meviatronic.zeus.helen;
+
+import java.util.*;
+import java.io.*;
+
+/**
+ * The <code>Tag</code> class provides methods to obtain the content of media tag
+ * informations, which describe possible extra informations of a media source.
+ * So it's possible to obtain, add and change tag informations with get and
+ * put methods. Predefined base hashkeys are defined in this class,
+ * but content related hashkeys must be defined inside derived
+ * classes. Note that all derived classes of this class are not used within
+ * this library itself. Instead media codec plug-ins of this library uses
+ * the tag system. Also the tag system can contain several tag formats like
+ * IDv1, IDv2 or Ogg Vorbis Tag, but a plug-in system of these different formats
+ * are not supported, because the tag system classes are referenced and instanced
+ * directly inside the codecs.
+ *
+ * <p>
+ * All tag related values of the hashkeys are <b>not</b> predefined with default values.
+ *
+ * <p>
+ * To create a hypothetical IDv2 tag plug-in it's required to derive
+ * this class according to the following listing:
+ *
+ * <blockquote>
+ * <pre>
+ * package org.ljmf.audio.codec.tag.id3v2;<p>
+ *
+ * import java.io.*;
+ * import java.util.*;
+ * import java.util.zip.*;<p>
+ *
+ * import org.ljmf.audio.codec.tag.*;<p>
+ *
+ * public final class MyIDv2Tag extends Tag {<p>
+ *
+ *     //====================<p>
+ *
+ *     public MyIDv2Tag(InputStream stream) throws IOException, TagException {
+ *         super();<p>
+ *
+ *         try {
+ *             readHeader(stream);
+ *         } catch (TagException e) {
+ *             close();
+ *             return;
+ *         }<p>
+ *
+ *         put(S_TAG_FORMAT, new String("ID3v2"));<p>
+ *
+ *         loadFrames(stream);<p>
+ *
+ *         buildKeyMapTable();<p>
+ *
+ *     }<p>
+ *
+ *     public String toString() {
+ *         // Override default implementation if neccessary.
+ *     }<p>
+ *
+ *     public Object clone() {
+ *         // Override default implementation if neccessary.
+ *     }<p>
+ *
+ *     //====================<p>
+ *
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * @author    Michael Scheerer
+ */
+public abstract class Tag extends Hashtable {
+	
+	/**
+	 * The predefined tag version key.
+	 */
+	public final static String S_TAG_FORMAT = "String tagFormat";
+
+	/**
+	 * The predefined tag value key.
+	 */
+	public final static String B_TAG = "Boolean tag";
+	
+	/**
+	 * The predefined album  value key.
+	 */
+	public final static String S_ALBUM = "String album";
+	
+	/**
+	 * The predefined artist value key.
+	 */
+	public final static String S_ARTIST = "String artist";
+	
+	/**
+	 * The predefined composer value key.
+	 */
+	public final static String S_COMPOSER = "String composer";
+	
+	/**
+	 * The predefined track (number) value key.
+	 */
+	public final static String S_TRACK = "String track";
+	
+	/**
+	 * The predefined year (of publishing) value key.
+	 */
+	public final static String S_YEAR = "String year";
+	
+	/**
+	 * The predefined date (of publishing) value key.
+	 */
+	public final static String S_DATE = "String date";
+	
+	/**
+	 * The predefined title value key.
+	 */
+	public final static String S_TITLE = "String title";
+	
+	/**
+	 * The predefined performer value key.
+	 */
+	public final static String S_PERFORMER = "String performer";
+	
+	/**
+	 * The predefined copyright text value key.
+	 */
+	public final static String S_COPYRIGHT = "String copyrightText";
+	
+	/**
+	 * The predefined genre text value key.
+	 */
+	public final static String S_GENRE = "String genre";
+		
+	/**
+	 * The predefined isrc (International Standard Recording Code) value key.
+	 */
+	public final static String S_ISRC = "String isrc";
+	
+	/**
+	 * The predefined encoder value key.
+	 */
+	public final static String S_ENCODER = "String encoder";
+	
+	
+	/**
+	 * Creates a new instance. Tag information is completely read the first time it
+	 * is requested and written after <code>update()</code>.
+	 *
+	 * @exception IOException   if I/O error occurs
+	 */
+	public Tag() throws IOException {}
+
+	/**
+	 * Frees all system resources, which are bounded to this object.
+	 */
+	public void close() {
+		clear();
+	}
+	
+	/**
+	 * Returns a <code>Hashtable</code> representation of this <code>Information</code> object.
+	 *
+	 * @return     a <code>Hashtable</code> representation of this <code>Information</code> object
+	 */
+	public Hashtable getHashtable() {
+		return (Hashtable) this.clone();
+	}
+}
+
+

Added: trunk/cronus/com/meviatronic/zeus/helen/TagException.java
===================================================================
--- trunk/cronus/com/meviatronic/zeus/helen/TagException.java	                        (rev 0)
+++ trunk/cronus/com/meviatronic/zeus/helen/TagException.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,50 @@
+/* Helena, a Comment decoder created by Michael Scheerer.
+ *
+ * Helena decoder (c) 2010 Michael Scheerer www.meviatronic.com
+ *
+ * Many thanks to
+ *   Monty <monty at xiph.org> and
+ *   The XIPHOPHORUS Company http://www.xiph.org/ .
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+   
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+package com.meviatronic.zeus.helen;
+
+/**
+ * This <code>Exception</code> should be thrown if tag informations can't be
+ * extracted
+ *
+ * @author    Michael Scheerer
+ */
+public final class TagException extends Exception {
+
+	/**
+	 * Constructs an instance of <code>TagException</code>.
+	 */
+	public TagException() {
+	}
+
+	/**
+	 * Constructs an instance of <code>TagException</code>.
+	 *
+	 * @param s  the exception message
+	 */
+	public TagException(java.lang.String s) {
+		super(s);
+	}
+}
+

Added: trunk/cronus/com/meviatronic/zeus/pollux/MemoryImageSource.java
===================================================================
--- trunk/cronus/com/meviatronic/zeus/pollux/MemoryImageSource.java	                        (rev 0)
+++ trunk/cronus/com/meviatronic/zeus/pollux/MemoryImageSource.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,88 @@
+/* Pollux, a fast Theora decoder created by Michael Scheerer.
+ *
+ * Pollux decoder (c) 2010 Michael Scheerer www.meviatronic.com
+ *
+ * Many thanks to
+ *   Monty <monty at xiph.org> and
+ *   The XIPHOPHORUS Company http://www.xiph.org/ .
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+   
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+package com.meviatronic.zeus.pollux;
+
+import java.awt.image.*;
+import java.util.*;
+
+/**
+ * This <code>MemoryImageSource</code> class is an optimized version of the
+ * origine <code>MemoryImageSource</code> class.
+ *
+ * @author Michael Scheerer
+ */
+final class MemoryImageSource implements ImageProducer {
+    private int width;
+    private int height;
+    private ColorModel model;
+    private Object pixels;
+    private int pixelscan;
+	private Output output;
+    
+    MemoryImageSource(int w, int h, ColorModel cm, Object pix, int scan, Output out) {
+		width = w;
+		height = h;
+		model = cm;
+		pixels = pix;
+		pixelscan = scan;
+		output = out;
+    }
+	
+    public void addConsumer(ImageConsumer ic) {
+    }
+
+    public boolean isConsumer(ImageConsumer ic) {
+        return false;
+    }
+
+    public void removeConsumer(ImageConsumer ic) {
+    }
+
+    public void requestTopDownLeftRightResend(ImageConsumer ic) {
+    }
+
+    public void startProduction(ImageConsumer ic) {
+		try {
+			synchronized(output) {
+				if(!output.display()) {
+					return;
+				}
+		 	}
+			ic.setDimensions(width, height);
+			ic.setColorModel(model);
+			ic.setHints(ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES | ImageConsumer.SINGLEFRAME | ImageConsumer.SINGLEPASS);
+			ic.imageComplete(ImageConsumer.STATICIMAGEDONE);
+			sendPixels(ic, 0, 0, width, height);
+		} catch (Exception e) {
+		}
+    }
+
+    private void sendPixels(ImageConsumer ic, int x, int y, int w, int h) {
+		int off = pixelscan * y + x;
+		ic.setPixels(x, y, w, h, model, ((int[]) pixels), off, pixelscan);
+    }
+}
+
+

Added: trunk/cronus/com/meviatronic/zeus/pollux/Output.java
===================================================================
--- trunk/cronus/com/meviatronic/zeus/pollux/Output.java	                        (rev 0)
+++ trunk/cronus/com/meviatronic/zeus/pollux/Output.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,448 @@
+/* Pollux, a fast Theora decoder created by Michael Scheerer.
+ *
+ * Pollux decoder (c) 2010 Michael Scheerer www.meviatronic.com
+ *
+ * Many thanks to
+ *   Monty <monty at xiph.org> and
+ *   The XIPHOPHORUS Company http://www.xiph.org/ .
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+   
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+package com.meviatronic.zeus.pollux;
+
+import com.meviatronic.zeus.castor.*;
+
+import java.io.*;
+import java.awt.*;
+import java.awt.image.*;
+
+import org.xiph.ogg.*;
+
+/**
+ * The <code>Output</code> class provides all necessary Theora video display related methods including
+ * the color model of Theora. Until now (Theora 1.1),
+ * the two defined color models are only different by the gamma values,
+ * which are not used here. They may be used from output devices (monitor, etc).
+ *
+ * @author	Michael Scheerer
+ */
+abstract class Output {
+	// 4.2 Color Space Conversions and Parameters
+	//
+ 	// YCbCr -> RGB mapping
+ 	//
+	// variables are {rU, bV, bgV, rgU}
+	//
+	// Formulars 4.1 - 4.6 used, with exception of the later Y handling
+	//
+	// rU = (255 / 224) * 2 * (1 - Kr)
+	// bV = (255 / 224) * 2 * (1 - Kb)
+	// bgV = (255 / 224) * 2 * (Kb / Kg) * (1 - Kb)
+	// rgU = (255 / 224) * 2 * (Kr / Kg) * (1 - Kr)
+ 	//
+	// where:
+	// 1. Y = Kr * R + Kg * G + Kb * B
+	// 2. Kr + Kg + Kb = 1
+	// 3.
+	//
+	// 0. Kr = 0.2990, Kg = 0.5870, Kb = 0.1140 // Undefined
+	// 1. Kr = 0.2990, Kg = 0.5870, Kb = 0.1140 // Rec. ITU-R BT.470-6 System M,    Gamma = 2.2
+	// 2. Kr = 0.2990, Kg = 0.5870, Kb = 0.1140 // Rec. ITU-R BT.470-6 System B, G, Gamma = 2.67 instead 2.8
+	// 3. Kr = 0.2990, Kg = 0.5870, Kb = 0.1140 // Reserved
+	//
+	private final static float YUV_TO_RGB_CONVERSION[] = {
+		1280F/802F, 661005F/327680F, -128375F/327680F, -266395F/327680F
+	};
+	
+	final static byte CHROMA444 = 3;
+	final static byte CHROMA422 = 2;
+	final static byte CHROMA420 = 0;
+	
+	final static byte INTRA_FRAME = 0;
+	
+	private final static int CLIP_MIN = 260;
+	private final static int CLIP_MAX = 780;
+	private final static int CLIP_RANGE = 1040;
+	
+	private static int clip_16[] = new int[CLIP_RANGE];
+	private static int clip_8[] = new int[CLIP_RANGE];
+	private static int clip[] = new int[CLIP_RANGE];
+	
+	private static int Y[] = new int[256 + CLIP_RANGE];
+	
+	private static int[] rU = new int[256 + CLIP_RANGE];
+	private static int[] bV = new int[256 + CLIP_RANGE];
+	private static int[] rgU = new int[256 + CLIP_RANGE];
+	private static int[] bgV = new int[256 + CLIP_RANGE];
+	
+	
+	private static ColorModel DEFAULT = new DirectColorModel(24, 0xFF0000, 0xFF00, 0xFF);
+    
+	private VideoReader info;
+	private int k, j;
+	private int u, v;
+	private int r, g, b, lum;
+	
+	private int dst[];
+	private int pixelRegion;
+	private int pixelOffset;
+	private int yCnt;
+	private int uvCnt;
+	private int clmCnt;
+	private int dstCnt;
+	private int lineCount;
+	private int pictureRegionX;
+	private int pictureRegionY;
+	private int pictureRegionW;
+	private int pictureRegionH;
+	private Rectangle imageRegion;
+	private Rectangle nullImageRegion;
+	private MemoryImageSource source;
+	
+	long granulepos;
+	int codedPictureHeight;
+	int chromaWidth;
+	int chromaHeight;
+	int chromaFormat;
+	int codedPictureWidth;
+	
+	short[] py, pu, pv;
+	
+	boolean skippedFrame;
+	int fType; // The frame type.
+	
+	static {
+		int i;
+		
+		for(i = -CLIP_MIN; i < CLIP_MAX; i++) {
+			clip_16[i + CLIP_MIN] = i >= 0 ? i <= 255 ? i << 16 : 0xFF0000 : 0;
+			clip_8[i + CLIP_MIN] = i >= 0 ? i <= 255 ? i << 8 : 0xFF00 : 0;
+			clip[i + CLIP_MIN] = i >= 0 ? i <= 255 ? i : 0xFF : 0;
+		}
+		for(i = 0; i < 256; i++) {
+			Y[i] = (int) (255F / 219F * (i - 16) + CLIP_MIN);
+			rU[i] = (int) (YUV_TO_RGB_CONVERSION[0] * (i - 128));
+			bV[i] = (int) (YUV_TO_RGB_CONVERSION[1] * (i - 128));
+			bgV[i] = (int) (YUV_TO_RGB_CONVERSION[2] * (i - 128));
+			rgU[i] = (int) (YUV_TO_RGB_CONVERSION[3] * (i - 128));
+
+		}
+	}
+	
+	/**
+	 * Constructs an instance of <code>Decoder</code> with a <code>MediaInformation</code>
+	 * object containing all necessary informations about the media source.
+	 *
+	 * @param info     the <code>VideoReader</code> object containing all
+	 *      necessary informations about the media source
+	 */
+	Output(VideoReader info) {
+		
+		this.info = info;
+		
+		codedPictureHeight = info.codedPictureHeight;
+		chromaFormat = info.chromaFormat;
+		codedPictureWidth = info.codedPictureWidth;
+		pictureRegionX = info.pictureRegionX;
+		pictureRegionY = info.pictureRegionY;
+		pictureRegionW = info.pictureRegionW;
+		pictureRegionH = info.pictureRegionH;
+		dst = new int[codedPictureWidth * codedPictureHeight];
+		imageRegion = new Rectangle(pictureRegionX, pictureRegionY, pictureRegionW, pictureRegionH);
+		nullImageRegion = new Rectangle(0, 0, 0, 0);
+		source = new MemoryImageSource(codedPictureWidth, codedPictureHeight, DEFAULT, dst, codedPictureWidth, this);
+	}
+	
+	private void progressive420() {
+		int yCntBuf, uvCntBuff, dstCntBuf;
+		
+		for (j = pixelOffset; j < pixelRegion; j += 4) {
+	  		lum = Y[py[yCnt]];
+      		u = pu[uvCnt];
+			v = pv[uvCnt];
+			r = rU[u];
+			g = rgU[u] + bgV[v];
+			b = bV[v];
+	  		dst[dstCnt] = clip_16[lum + r] | clip_8[lum + g] | clip[lum + b];
+			
+			yCntBuf = yCnt + codedPictureWidth;
+			dstCntBuf = dstCnt - codedPictureWidth;
+
+			lum = Y[py[yCntBuf]];
+			
+			dst[dstCntBuf] = clip_16[lum + r] | clip_8[lum + g] | clip[lum + b];
+			
+			yCntBuf = yCnt + 1;
+			dstCntBuf = dstCnt + 1;
+
+			lum = Y[py[yCntBuf]];
+			
+			dst[dstCntBuf] = clip_16[lum + r] | clip_8[lum + g] | clip[lum + b];
+			
+			yCntBuf = yCntBuf + codedPictureWidth;
+			dstCntBuf = dstCntBuf - codedPictureWidth;
+
+			lum = Y[py[yCntBuf]];
+			
+			dst[dstCntBuf] = clip_16[lum + r] | clip_8[lum + g] | clip[lum + b];
+			
+			clmCnt += 2;
+			yCnt += 2;
+			dstCnt += 2;
+			uvCnt++;
+			if (clmCnt == codedPictureWidth) {
+				clmCnt = 0;
+				yCnt += codedPictureWidth;
+				dstCnt -= 3 * codedPictureWidth;
+			}
+		}
+	}
+	
+	private void progressive422() {
+		int yCntBuf, uvCntBuff, dstCntBuf;
+		
+		for (j = pixelOffset; j < pixelRegion; j += 4) {
+	  		lum = Y[py[yCnt]];
+      		u = pu[uvCnt];
+			v = pv[uvCnt];
+			r = rU[u];
+			g = rgU[u] + bgV[v];
+			b = bV[v];
+	  		dst[dstCnt] = clip_16[lum + r] | clip_8[lum + g] | clip[lum + b];
+			
+			yCntBuf = yCnt + codedPictureWidth;
+			dstCntBuf = dstCnt - codedPictureWidth;
+
+			lum = Y[py[yCntBuf]];
+			
+			dst[dstCntBuf] = clip_16[lum + r] | clip_8[lum + g] | clip[lum + b];
+			
+			yCntBuf = yCnt + 1;
+			dstCntBuf = dstCnt + 1;
+
+			lum = Y[py[yCntBuf]];
+			
+			u = pu[uvCnt];
+			v = pv[uvCnt];
+			r = rU[u];
+			g = rgU[u] + bgV[v];
+			b = bV[v];
+
+			dst[dstCntBuf] = clip_16[lum + r] | clip_8[lum + g] | clip[lum + b];
+			
+			yCntBuf = yCntBuf + codedPictureWidth;
+			dstCntBuf = dstCntBuf - codedPictureWidth;
+
+			lum = Y[py[yCntBuf]];
+			
+			dst[dstCntBuf] = clip_16[lum + r] | clip_8[lum + g] | clip[lum + b];
+			
+			clmCnt += 2;
+			yCnt += 2;
+			dstCnt += 2;
+			uvCnt++;
+			if (clmCnt == codedPictureWidth) {
+				uvCnt += chromaWidth;
+				clmCnt = 0;
+				yCnt += codedPictureWidth;
+				dstCnt -= 3 * codedPictureWidth;
+			}
+		}
+	}
+	
+	private void progressive444() {
+		int yCntBuf, uvCntBuff, dstCntBuf;
+		
+		for (j = pixelOffset; j < pixelRegion; j += 4) {
+	  		lum = Y[py[yCnt]];
+      		u = pu[uvCnt];
+			v = pv[uvCnt];
+			r = rU[u];
+			g = rgU[u] + bgV[v];
+			b = bV[v];
+	  		dst[dstCnt] = clip_16[lum + r] | clip_8[lum + g] | clip[lum + b];
+			
+			yCntBuf = yCnt + codedPictureWidth;
+			dstCntBuf = dstCnt - codedPictureWidth;
+
+			lum = Y[py[yCntBuf]];
+			uvCntBuff = uvCnt + chromaWidth;
+			u = pu[uvCntBuff];
+			v = pv[uvCntBuff];
+			r = rU[u];
+			g = rgU[u] + bgV[v];
+			b = bV[v];
+			
+			dst[dstCntBuf] = clip_16[lum + r] | clip_8[lum + g] | clip[lum + b];
+			
+			yCntBuf = yCnt + 1;
+			dstCntBuf = dstCnt + 1;
+
+			lum = Y[py[yCntBuf]];
+			uvCnt++;
+			u = pu[uvCnt];
+			v = pv[uvCnt];
+			r = rU[u];
+			g = rgU[u] + bgV[v];
+			b = bV[v];
+			
+			dst[dstCntBuf] = clip_16[lum + r] | clip_8[lum + g] | clip[lum + b];
+			
+			yCntBuf = yCntBuf + codedPictureWidth;
+			dstCntBuf = dstCntBuf - codedPictureWidth;
+
+			lum = Y[py[yCntBuf]];
+			uvCntBuff = uvCnt + chromaWidth; // still incremented
+			u = pu[uvCntBuff];
+			v = pv[uvCntBuff];
+			r = rU[u];
+			g = rgU[u] + bgV[v];
+			b = bV[v];
+
+			dst[dstCntBuf] = clip_16[lum + r] | clip_8[lum + g] | clip[lum + b];
+			
+			clmCnt += 2;
+			yCnt += 2;
+			dstCnt += 2;
+			uvCnt++;
+			if (clmCnt == codedPictureWidth) {
+				uvCnt += chromaWidth;
+				clmCnt = 0;
+				yCnt += codedPictureWidth;
+				dstCnt -= 3 * codedPictureWidth;
+			}
+		}
+	}
+	
+	abstract boolean display();
+
+	void display(int offset, int range) {
+		pixelRegion = range;
+		pixelOffset = offset;
+		clmCnt = range % codedPictureWidth;
+		yCnt = offset;
+		uvCnt = offset >> 2;
+		if (chromaFormat == CHROMA422) {
+			uvCnt = offset >> 1;
+		} else if (chromaFormat == CHROMA444) {
+			uvCnt = offset;
+		}
+		dstCnt = codedPictureWidth * (codedPictureHeight - offset / codedPictureWidth - 1);
+		
+		if (chromaFormat == CHROMA420) {
+			progressive420();
+		} else if (chromaFormat == CHROMA422) {
+			progressive422();
+		} else {
+			progressive444();
+		}
+	}
+	
+	/**
+	 * Returns the image region boundary to update an image region.
+	 * With this boundary the actual image region
+	 * of the selected image can be displayed
+	 * on a video screen display with more performance
+	 * than updating the hole image.
+	 *
+	 * @return  the image region boundary to update an image region
+	 */
+	public final Rectangle getImageRegion() {
+		if (skippedFrame) {
+			return nullImageRegion;
+		}
+		return imageRegion;
+	}
+	
+	abstract void decodeImage() throws IOException, EndOfMediaException, EndOfPacketException;
+	
+	public final boolean isKeyFrame() {
+		return fType == INTRA_FRAME ? true : false;
+  	}
+	
+	public final ImageProducer synthesis(Packet op) throws IOException, EndOfPacketException {
+    	info.loadPacket(op.packetBase, op.packet, op.bytes);
+		
+		synchronized(this) {
+			decodeImage();
+		}
+		
+		if(op.granulepos > -1){
+        	granulepos = op.granulepos;
+	  	} else {
+		  	if (granulepos == -1){
+          		granulepos = 0;
+        	} else {
+				if (fType == INTRA_FRAME){
+            		long frames = granulepos & VideoReader.BITMASK[info.keyFrameNumberGranuleShift];
+					
+					granulepos >>>= info.keyFrameNumberGranuleShift;
+            		granulepos += frames + 1;
+            		granulepos <<= info.keyFrameNumberGranuleShift;
+          		} else {
+			  		granulepos++;
+		  		}
+        	}
+		}
+		CropImageFilter cropFilter = new CropImageFilter(pictureRegionX, pictureRegionY, pictureRegionW, pictureRegionH);
+		return new FilteredImageSource(source, cropFilter);
+	}
+	
+	public final double granuleTime(long granulePosition) {
+		if(granulePosition >= 0) {
+			return getFrameCount(granulePosition, info.keyFrameNumberGranuleShift, 0) / info.frameRate;
+    	}
+    	return -1;
+  	}
+	
+	private static int getFrameCount (long granulePosition, int keyFrameNumberGranuleShift, int versionRevisionNumber) {
+		if (versionRevisionNumber > 0) {
+			return (int) (granulePosition & VideoReader.BITMASK[keyFrameNumberGranuleShift]) + (int) (granulePosition >>> keyFrameNumberGranuleShift) - 1;
+		}
+		return (int) (granulePosition & VideoReader.BITMASK[keyFrameNumberGranuleShift]) + (int) (granulePosition >>> keyFrameNumberGranuleShift);
+	}
+
+	/**
+	 * Frees all system resources, which are bounded to this object.
+	 */
+	public void close() {
+		dst = null;
+		imageRegion = null;
+		nullImageRegion = null;
+		py = null;
+		pu = null;
+		pv = null;
+	}
+	
+	final int get1() throws IOException, EndOfPacketException {
+		return info.get1();
+	}
+	
+	final int get(int i) throws IOException, EndOfPacketException {
+		return info.get(i);
+	}
+
+	final int getCodeWord(int bookNumber) throws IOException, EndOfPacketException {
+		return info.getCodeWord(bookNumber);
+	}
+	
+	final static int ilog(int v) {
+		return AudioReader.ilog(v);
+	}
+}
+
+

Added: trunk/cronus/com/meviatronic/zeus/pollux/README.txt
===================================================================
--- trunk/cronus/com/meviatronic/zeus/pollux/README.txt	                        (rev 0)
+++ trunk/cronus/com/meviatronic/zeus/pollux/README.txt	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,93 @@
+	Pollux, a fast Theora decoder created by Michael Scheerer.
+	Pollux decoder (c) 2009 Michael Scheerer www.meviatronic.com
+
+	Many thanks to
+	Monty <monty at xiph.org> and
+	The XIPHOPHORUS Company http://www.xiph.org/ .
+	
+	Pollux's IDCT based on:
+	The official Theora specification.
+	
+	Features of Pollux's enhanced IDCT:
+	- inplacing.
+ 	- higher degree of content adaptive calculation.
+ 	  Dr. m. m. Timothy B. Terriberry pointed out, that this approach is only helpful in case
+ 	  of systems and compilers, which don't have or exploit
+ 	  the SIMD (Single Instruction Multiple Data) computer architecture.
+ 	
+ 	Pollux's colorspace transformation based on:
+	The official Theora specification.
+	
+	Pollux's CRC32 based on (but here used inside the Ogg Framing):
+	public domain code by
+	Ross Williams (ross at guest.adelaide.edu.au).
+	A 32 bit CRC value (direct algorithm, initial val and final XOR = 0, generator polynomial=0x04c11db7) is used.
+	The value is computed over the entire header (with the CRC field in the header set to zero)
+	and then continued over the page. The CRC field is then filled with the computed value.
+
+	Pollux's huffman decoding based on:
+ 	Self developed algorithm (deflated binary tree approach -
+	a kind of a 2 pow n compression), therefore there's no reference.
+	Dr. m. m. Timothy B. Terriberry pointed out, that this kind of approach is at least since 1993 a
+	reinvention:
+	R. Hashemian, "High Speed Search and Memory Efficient
+    Huffman Coding," 1993.
+
+	Pollux's bitstream decoding based on:
+ 	Self developed algorithm, therefore there's no reference.
+ 	
+	Summary:
+	
+	Advantages of Pollux over the current reference decoder (libTheora 1.1.1):
+	- content adaptive color space transformation.
+ 	- content adaptive block copying.
+ 	- content adaptive motion prediction (sparseDC mode).
+ 	- higher degree of content adaptive IDCT on non SIMD architectures.
+ 	- higher degree of content adaptive edge detection and deblock filtering.
+ 	- sparseDC mode.
+ 	  Dr. m. m. Timothy B. Terriberry pointed out, that there is
+ 	  a separation of the MC from the DCT in developing for a future reference decoder version,
+ 	  which even avoids such a special sparseDC mode with the same amount of
+ 	  reducing the calculation amount.
+ 	- avoiding a two pass coefficient decoding.
+ 	
+ 	Advantages of Pollux over an older Java version (called JTheora) of the reference decoder:
+ 	- drastical reduced binary or bytecode size
+ 	  (from 80 Kbyte Java bytecode ripped of encoder stuff to only 50 Kbyte).
+ 	- support of the specified truncated packet operation.
+ 	- better huffman decoding.
+	- memory consumption is lower (under Java according to the taskmanager).
+	- performance consumption is lower (under Java according to the taskmanager).
+ 	- "feeled" performance improvement of full scaled video playback is nearly 80%
+ 	  on a single core Pentium M 1.4 GHZ machine.
+ 	- content adaptive color space transformation.
+ 	- content adaptive motion prediction (sparseDC mode).
+ 	- higher degree of content adaptive IDCT.
+ 	- higher degree of content adaptive edge detection and deblocking.
+ 	- color space transformation is working in a separate thread - yielding a
+ 	  better load balancing of the CPU utilization on multicore/multithread machines
+ 	  or in case of CPU loads of over 100%.
+ 	- sparseDC mode.
+ 	- support of the pixel formats 4:4:4 and 4:2:2.
+ 	- elimination of the "64 * number of blocks"-fold performance bottleneck if-condition
+ 	  inside the coefficient decoding.
+ 	
+ 	The code size and/or memory consumption may be of interest for
+ 	firmware developers of hardware players.
+ 	
+ 	A firmware port of Pollux should replace the recursions during the
+ 	Huffman decoder initialization. Under Java, this recursion based initialization
+ 	is faster than the non recursion version.
+ 	
+ 	Todo:
+ 	
+ 	Exact performance tests against the actual reference decoder.
+ 	
+ 	Because of the small codesize of Pollux, the decoder may
+ 	be the right one for e.g. firmware/HDL/ASIC developers.
+ 	
+ 	
+ 	Appreciations:
+ 	
+ 	Very thanks to Dr. m. m. Timothy B. Terriberry for reviewing and error correcting
+ 	the README and Pollux decoder.

Added: trunk/cronus/com/meviatronic/zeus/pollux/TheoraDecoder.java
===================================================================
--- trunk/cronus/com/meviatronic/zeus/pollux/TheoraDecoder.java	                        (rev 0)
+++ trunk/cronus/com/meviatronic/zeus/pollux/TheoraDecoder.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,2416 @@
+/* Pollux, a fast Theora decoder created by Michael Scheerer.
+ *
+ * Pollux decoder (c) 2010 Michael Scheerer www.meviatronic.com
+ *
+ * Many thanks to
+ *   Monty <monty at xiph.org> and
+ *   The XIPHOPHORUS Company http://www.xiph.org/ .
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+   
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+package com.meviatronic.zeus.pollux;
+
+import com.meviatronic.zeus.castor.*;
+
+import java.io.*;
+
+/**
+ * The <code>TheoraDecoder</code> class is the main video decoder class.
+ *
+ * @author  Michael Scheerer
+ */
+public final class TheoraDecoder extends Output  {
+	
+	private final static int CLIP_MIN = 33024;
+	private final static int CLIP_MAX = 33024;
+	private final static int CLIP_RANGE = 66048;
+	
+	private static short clip[] = new short[CLIP_RANGE];
+	
+	private final static int FILTER_MIN = 256;
+	private final static int FILTER_RANGE = 512;
+	
+	private final static int PLANE_CLIP_MIN = 16;
+	private final static int PLANE_CLIP_MAX = 16;
+	private final static int PLANE_CLIP_RANGE = 32;
+	
+	private final static byte INTER_NOMV = 0;
+	private final static byte INTRA = 1;
+	private final static byte INTER_MV = 2;
+	private final static byte INTER_MV_LAST = 3;
+	private final static byte INTER_MV_LAST2 = 4;
+	private final static byte INTER_GOLDEN_NOMV = 5;
+	private final static byte INTER_GOLDEN_MV = 6;
+	private final static byte INTER_MV_FOUR = 7;
+	
+	private final static byte INTRA_FRAME = 0;
+	private final static byte PREVIOUS_REFERENCE_FRAME_INDEX = 1;
+	private final static byte GOLDEN_REFERENCE_FRAME_INDEX = 2;
+	
+	private final static int C1 = 64277;
+	private final static int S7 = 64277;
+	private final static int C2 = 60547;
+	private final static int S6 = 60547;
+	private final static int C3 = 54491;
+	private final static int S5 = 54491;
+	private final static int C4 = 46341;
+	private final static int S4 = 46341;
+	private final static int C5 = 36410;
+	private final static int S3 = 36410;
+	private final static int C6 = 25080;
+	private final static int S2 = 25080;
+	private final static int C7 = 12785;
+	private final static int S1 = 12785;
+		
+	private final static short[] ZERO_SHORTS = new short[64];
+	
+	private final static byte NATURAL_ORDER_SCAN[] = {
+		0, 1, 2, 3, 4, 5, 6, 7,
+		8, 9, 10, 11, 12, 13, 14, 15,
+		16, 17, 18, 19, 20, 21, 22, 23,
+		24, 25, 26, 27, 28, 29, 30, 31,
+		32, 33, 34, 35, 36, 37, 38, 39,
+		40, 41, 42, 43, 44, 45, 46, 47,
+		48, 49, 50, 51, 52, 53, 54, 55,
+		56, 57, 58, 59, 60, 61, 62, 63,
+		63, 63, 63, 63, 63, 63, 63, 63,
+		63, 63, 63, 63, 63, 63, 63, 63,
+		63, 63, 63, 63, 63, 63, 63, 63,
+		63, 63, 63, 63, 63, 63, 63, 63,
+		63, 63, 63, 63, 63, 63, 63, 63,
+		63, 63, 63, 63, 63, 63, 63, 63,
+		63, 63, 63, 63, 63, 63, 63, 63,
+		63, 63, 63, 63, 63, 63, 63, 63
+	};
+	private final static byte INVERSE_ZIG_ZAG_SCAN[] = {
+		 0,  1,  8, 16,  9,  2,  3, 10,
+		17, 24, 32, 25, 18, 11,  4,  5,
+		12, 19, 26, 33, 40, 48, 41, 34,
+		27, 20, 13,  6,  7, 14, 21, 28,
+		35, 42, 49, 56, 57, 50, 43, 36,
+		29, 22, 15, 23, 30, 37, 44, 51,
+		58, 59, 52, 45, 38, 31, 39, 46,
+		53, 60, 61, 54, 47, 55, 62, 63
+	};
+	private final static byte ZIG_ZAG_TO_NATURAL_ORDER_ROW_MAPPING[] = {
+		 0,  0,  1,  2,  2,  2,  2,  2,
+		 2,  3,  4,  4,  4,  4,  4,  4,
+		 4,  4,  4,  4,  5,  6,  6,  6,
+		 6,  6,  6,  6,  6,  6,  6,  6,
+		 6,  6,  6,  7,  7,  7,  7,  7,
+		 7,  7,  7,  7,  7,  7,  7,  7,
+		 7,  7,  7,  7,  7,  7,  7,  7,
+		 7,  7,  7,  7,  7,  7,  7,  7
+	};
+	// [yRemainder][xRemainder][bi]
+	private final static byte SUPERBLOCK_X_BLOCKS_OFFSET[][][] = {
+	  {{0},
+		   {0, 1},
+				  {0, 1, 2},
+				  			{0, 1, 2, 3}},
+	  {{0,
+	    0},
+		   {0, 1,
+		    1, 0},
+				  {0, 1, 1,
+				   0, 2, 2},
+							{0, 1, 1, 0,
+							 3, 2, 2, 3}},
+	  {{0,
+	    0,
+	    0},
+		   {0, 1,
+		    1, 0,
+		    0, 1},
+				  {0, 1, 1,
+				   0, 0, 1,
+				   2, 2, 2},
+				  			{0, 1, 1, 0,
+							 0, 1, 2, 3,
+							 3, 2, 2, 3}},
+	  {{0,
+	    0,
+	    0,
+	    0},
+		   {0, 1,
+			1, 0,
+	      	0, 0,
+		  	1, 1},
+			  	  {0, 1, 1,
+				   0, 0, 0,
+				   1, 1, 2,
+				   2, 2, 2},
+							{0, 1, 1, 0,
+							 0, 0, 1, 1,
+							 2, 2, 3, 3,
+							 3, 2, 2, 3}}
+	};
+	// [yRemainder][xRemainder][bi]
+	private final static byte SUPERBLOCK_Y_BLOCKS_OFFSET[][][] = {
+	  {{0},
+		   {0, 0},
+				  {0, 0, 0},
+				  			{0, 0, 0, 0}},
+	  {{0,
+	    1},
+		   {0, 0,
+		    1, 1},
+				  {0, 0, 1,
+				   1, 1, 0},
+							{0, 0, 1, 1,
+							 1, 1, 0, 0}},
+	  {{0,
+	    1,
+	    2},
+		   {0, 0,
+		    1, 1,
+		    2, 2},
+				  {0, 0, 1,
+				   1, 2, 2,
+				   2, 1, 0},
+							{0, 0, 1, 1,
+							 2, 2, 2, 2,
+							 1, 1, 0, 0}},
+	  {{0,
+	    1,
+	    2,
+	    3},
+		   {0, 0,
+		    1, 1,
+		    2, 3,
+		    3, 2},
+				  {0, 0, 1,
+				   1, 2, 3,
+				   3, 2, 2,
+				   3, 1, 0},
+							{0, 0, 1, 1,
+							 2, 3, 3, 2,
+							 2, 3, 3, 2,
+							 1, 1, 0, 0}}
+	};
+	private final static byte HUFFMANCODETABLE7_7RSTART[] = {
+		1, 2, 4, 6, 10, 18, 34
+	};
+	private final static byte HUFFMANCODETABLE7_7RBITS[] = {
+		0, 1, 1, 2, 3, 4, 12
+	};
+	private final static byte HUFFMANCODETABLE7_11RSTART[] = {
+		1, 3, 5, 7, 11, 15
+	};
+	private final static byte HUFFMANCODETABLE7_11RBITS[] = {
+		1, 1, 1, 2, 2, 4
+	};
+	private final static byte HUFFMANCODETABLE7_14[][] = {
+		{0, 0, 0, 0, 0, 0, 0, 0},
+		{3, 4, 2, 0, 1, 5, 6, 7}, {3, 4, 0, 2, 1, 5, 6, 7},
+		{3, 2, 4, 0, 1, 5, 6, 7}, {3, 2, 0, 4, 1, 5, 6, 7},
+		{0, 3, 4, 2, 1, 5, 6, 7}, {0, 5, 3, 4, 2, 1, 6, 7}
+	};
+	// Negative values assign to table columns and the to read bit length,
+	// with the exception of -2, where column is 2 and the to read bit length only 1.
+	// Positive values must be subtracted with 31 to produce the final result.
+	// The lookup procedure starts with the reading of 3 bits. These values are the indices for column 0.
+	private final static byte HUFFMANCODETABLE7_23[][] = {
+		{31, 32, 30, -1, -2, -3, -4, -5},
+		{33, 29},
+		{34, 28},
+		{35, 27, 36, 26, 37, 25, 38, 24},
+		{39, 23, 40, 22, 41, 21, 42, 20, 43, 19, 44, 18, 45, 17, 46, 16},
+		{47, 15, 48, 14, 49, 13, 50, 12, 51, 11, 52, 10, 53,  9, 54,  8, 55,  7, 56,  6, 57,  5, 58,  4, 59,  3, 60,  2, 61,  1, 62,  0},
+	};
+	private final static byte TOKEN_TABLE7_33[][] = {
+		{0, 0, 0, 2, 3,  4, 12, 3, 6, 0,  0, 0,  0, 1, 1, 1, 1, 2, 3,  4,  5,  6, 10, 1, 1, 1, 1, 1, 3,  4,  2, 3},
+		{0, 0, 0, 3, 7, 15, -1, 0, 0, 1, -1, 2, -2, 3, 4, 5, 6, 7, 9, 13, 21, 37, 69, 1, 2, 3, 4, 5, 6, 10,  0, 1},
+		{0, 0, 0, 0, 0, 0,   0, 1, 1, 2,  2, 2,  2, 2, 2, 2, 2, 2, 2,  2,  2,  2,  2, 3, 3, 3, 3, 3, 3,  3,  4, 4}
+	};
+	private final static byte HUFFMAN_GROUP_TABLE7_42[] = {
+		0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,
+		3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+		4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+		4, 4, 4, 4
+	};
+	private final static byte CODE_MODE_TABLE7_46[] = {
+		1, 0, 1, 1, 1, 2, 2, 1
+	};
+	private final static short DCPREDICTORS_WEIGHTS_AND_DIVISORS_TABLE7_47[][] = {
+		{ 0,   0,   0,   0,   0},
+		{ 1,   0,   0,   0,   1},
+		{ 1,   0,   0,   0,   1},
+		{ 1,   0,   0,   0,   1},
+		{ 1,   0,   0,   0,   1},
+		{ 1,   1,   0,   0,   2},
+		{ 0,   1,   0,   0,   1},
+		{29, -26,  29,   0,  32},
+		{ 1,   0,   0,   0,   1},
+		{75,  53,   0,   0, 128},
+		{ 1,   1,   0,   0,   2},
+		{75,   0,  53,   0, 128},
+		{ 1,   0,   0,   0,   1},
+		{75,   0,  53,   0, 128},
+		{ 3,  10,   3,   0,  16},
+		{29, -26,  29,   0,  32}
+	};
+	// [yRemainder][xRemainder][bi] // index plus/minus one to avoid -0 / +0!
+	// Note, that Hilbert curve pattern was choosen,
+	// because the fractal dimension of a Hilbert curve aproximates to 2.
+	private final static byte CHROMA_420_BI_TO_MBI_MAPPING[][][] = {
+	   {{ 1            },
+	    { 1,  2        },
+	    { 1,  2,  3    },
+	    { 1,  2,  3,  4}},
+	   {{ 1,  2,       },
+	    { 1,  4,  3,  2},
+	    { 1,  4,  3,  2,
+	      6,  5        },
+	    { 1,  4,  3,  2,
+	      7,  6,  5,  8}},
+	   {{ 1,  2, -1    },
+	    { 1,  4,  3,  2,
+	     -1, -2       },
+	    { 1,  4,  3,  2,
+	     -1, -2, -3,
+	      6,  5        },
+	    { 1,  4,  3,  2,
+	     -1, -2,
+	     -3, -4,
+	      7,  6,  5,  8}},
+	   {{ 1,  2, -1, -2},
+	    { 1,  4,  3,  2,
+	     -1, -2, -3, -4},
+	    { 1,  4,  3,  2,
+	     -1, -2, -3, -4,
+	     -5, -6,  6,  5},
+	    { 1,  4,  3,  2,
+	     -1, -2, -3, -4,
+	     -5, -6, -7, -8,
+	      7,  6,  5,  8}}
+	};
+	// [yRemainder / 2][xRemainder][bi] // index plus/minus one to avoid -0 / +0!
+	// Note, that Hilbert curve pattern was choosen,
+	// because the fractal dimension of a Hilbert curve aproximates to 2.
+	private final static byte CHROMA_422_BI_TO_MBI_MAPPING[][][] = {
+	   {{ 1,  1        },
+	    { 1,  2,  2,  1},
+	    { 1,  2,  2,  1,
+	      3,  3,       },
+	    { 1,  2,  2,  1,
+	      4,  3,  3,  4}},
+	   {{ 1,  1,  2,  2},
+	    { 1,  4,  4,  1,
+	      2,  2,  3,  3},
+	    { 1,  4,  4,  1,
+	      2,  2,  3,  3,
+	      6,  6,  5,  5},
+	    { 1,  4,  4,  1,
+	      2,  2,  3,  3,
+	      6,  6,  7,  7,
+	      8,  5,  5,  8}}
+	};
+	// The first index gets 1 if the superblock has 4 macro blocks or is truncated
+	// along the x-axis or 0 if the the superblock is truncated along the y-axis,
+	// the second index gets the luma macro block indices (lmbi) and the third index
+	// gets the block indices (coding order) and the result are raster ordered block indices.
+	private final static byte CODING_ORDER_TO_BLOCK_RASTER_ORDER[][][] = {
+	   {{ 0,  1,  3,  2},
+	    { 2,  3,  1,  0}},
+	   {{ 0,  1,  3,  2},
+	    { 0,  3,  1,  2},
+	    { 0,  3,  1,  2},
+	    { 2,  3,  1,  0}}
+	};
+	// The first index gets 1 if the superblock has 4 macro blocks or is truncated
+	// along the x-axis or 0 if the the superblock is truncated along the y-axis,
+	// the second index gets the block indices (coding order).
+	private final static byte CHROMA_422_MOTIONVECTOR_SELECTION[][] = {
+	    { 4,  4,  5,  5,
+	      5,  5,  4,  4},
+	    { 4,  4,  5,  5,
+	      4,  5,  5,  4,
+	      4,  5,  5,  4,
+	      5,  5,  4,  4}
+	};
+	// [chromaDecimation][mva + 31]
+	private final static byte MOTIONVECTOR_MAPPING_BASE[][] = {
+	   {-15, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10,  -9,  -9,  -8,
+	     -8,  -7,  -7,  -6,  -6,  -5,  -5,  -4,  -4,  -3,  -3,  -2,  -2,  -1,  -1,   0,
+	      0,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   7,
+	      8,   8,   9,   9,  10,  10,  11,  11,  12,  12,  13,  13,  14,  14,  15,  15},
+	   { -7,  -7,  -7,  -7,  -6,  -6,  -6,  -6,  -5,  -5,  -5,  -5,  -4,  -4,  -4,
+	     -4,  -3,  -3,  -3,  -3,  -2,  -2,  -2,  -2,  -1,  -1,  -1,  -1,   0,   0,   0,
+	      0,   0,   0,   0,   1,   1,   1,   1,   2,   2,   2,   2,   3,   3,   3,   3,
+ 	      4,   4,   4,   4,   5,   5,   5,   5,   6,   6,   6,   6,   7,   7,   7,   7}
+	};
+	// [chromaDecimation][mva + 31]
+	private final static byte MOTIONVECTOR_MAPPING_DIFF[][] = {
+	   { -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,
+ 	      0,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,
+ 	      0,   1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   1,
+ 	      0,   1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   1},
+	   { -1,  -1,  -1,   0,  -1,  -1,  -1,   0,  -1,  -1,  -1,   0,  -1,  -1,  -1,
+ 	      0,  -1,  -1,  -1,   0,  -1,  -1,  -1,   0,  -1,  -1,  -1,   0,  -1,  -1,  -1,
+ 	      0,   1,   1,   1,   0,   1,   1,   1,   0,   1,   1,   1,   0,   1,   1,   1,
+ 	      0,   1,   1,   1,   0,   1,   1,   1,   0,   1,   1,   1,   0,   1,   1,   1}
+	};
+	private final static short[] ONE_HUNDRED_TWENTYEIGHT_INTS = {
+		128, 128, 128, 128, 128, 128, 128, 128
+	};
+	private short[][][][] qmat; // A 64-element array of quantization values for each DCT coefficient in natural order.
+	private boolean initialized;
+	private short[][] image; // An array containing the contents of all planes of the current frame.
+	private short[][] ref; // An array containing the contents of all planes of the reference frame (past frame).
+	private short[][] goldRef; // An array containing the contents of all planes of the golden reference frame (past key frame).
+	private byte[] mAlphabet = new byte[8]; // Integer array - The list of modes corresponding to each Huffman code.
+	private int nbs; // The number of blocks. // Porting problem: Should be long
+	private int nsbs; // The number of super blocks.
+	private int nlsbs; // The number of luma plane super blocks.
+	private int ncsbs; // The number of chroma plane super blocks.
+	private int nmbs; // The number of macro blocks.
+	private int fType; // The frame type.
+	private int nqis; // The number of qi values.
+	private int[] qis = new int[3]; // A NQIS-element array of qi values.
+	private byte[] tis; // A NSBS*NBSPS-element array of the current token index for each block.
+	private byte[][] bCoded; // A NSBS/NBSPS array indicating which blocks are coded.
+	private int[] cbIndex; // A NBS-element array of flags (sbi | bi) coding coded block indices. // Porting problem: Should be long[]
+	private Node qcbIndex;
+	private Node root;
+	private int[] ncbIndex; // A NBS-element array of flags (sbi | bi) coding not coded intra frame block indices. // Porting problem: Should be long[]
+	private int[] cifbIndex; // A NBS-element array of flags (sbi | bi) coding coded intra frame block indices. // Porting problem: Should be long[]
+	private int[] ncifbIndex; // A NBS-element array of flags (sbi | bi) coding not coded block indices. // Porting problem: Should be long[]
+	private int[] mbiToSbiLmbiMapping; // A NMBS-element array of flags (sbi | lmbi | ySbRemainder / 2) coding macro block indices.
+	private int[] biToMbiMapping; // A NSBS*NBSPS array of macro block indices.
+	private int[][][] rasterOrderToCodedOrderMapping; // A NPLS/NBY/NBX array of flags (sbi | bi).
+	private byte[] mbMode; // A NMBS-element array of coding modes for each macro block.
+	private byte[][][] mVects; // A NMBS/NBSPMB/MVECXY array of motion vectors.
+	private byte[] qiis; // A NBS-element array of qii values for each block.
+	private int ncbs; // Number of coded blocks. // Porting problem: Should be long
+	private int nncbs; // Number of not coded blocks. // Porting problem: Should be long
+	private short[][] coeffs; // A NSBS*NBSPS/64 array of quantized DCT coefficient values for each block in zig-zag order.
+	private byte[] nCoeffs; // A NSBS*NBSPS-element array of the coefficient count for each block.
+	private short[] lastDc = new short[3]; // A 3-element array containing the most recently decoded DC value, one for inter mode and for each reference frame.
+	private short[] coeffsValues = new short[4]; // A 4-element array of the neighboring DC coefficient values to apply to each DC value.
+	private int[] spipp; // A NSBS-element array of the spi's of each single plane.
+	private int[] pli; // A NSBS-element array of the plane index values.
+	private int y0, y1, y2, y3, y4, y5, y6, y7; // An 8-element array of 1D iDCT input values.
+	private int[][] planeWidthClip; // An array for clipping/clamping plane sizes for each plane. The third plane clip array is a reused second one.
+	private int[][] planeHeightClip; // An array for clipping/clamping plane sizes for each plane. The third plane clip array is a reused second one.
+	private byte[] lflims; // A 64-element array of loop filter limit values.
+	private byte[] sbpCodedBits; // Bit string of a decoded set of partial coded super block flags. // Porting problem: Should be long
+	private byte[] sbfCodedBits; // Bit string of a decoded set of full coded super block flags. // Porting problem: Should be long
+	private byte[] bits; // Bit string of a decoded set of flags. // Porting problem: Should be long
+	private int eobs; // The remaining length of the current EOB run. // Porting problem: Should be long
+	private int[] blockCoordinateX; // A NSBS*NBSPS-element array of the horizontal pixel indices of the lower-left corner of the current block.
+	private int[] blockCoordinateY; // A NSBS*NBSPS-element array of the vertical pixel indices of the lower-left corner of the current block.
+	private short[][] lflim = new short[64][FILTER_RANGE];
+	private int[] dqc = new int[64]; // An array of dequantized DCT coefficients in natural order.
+	private boolean[] isNb = new boolean[4]; // Is there a neighbor block for inverting the DC Prediction Process?
+	private int sbChromaWidth;
+	private int sbChromaHeight;
+	private int sbLumaWidth;
+	private int sbLumaHeight;
+	private int bChromaWidth;
+	private int bChromaHeight;
+	private int bLumaWidth;
+	private int bLumaHeight;
+	private int mbWidth;
+	private int mbHeight;
+	private byte[] zeroBytes;
+	private byte[] oneBytes;
+	private byte[][] oneBytesBytes;
+	private int pixelOffset;
+	private int pixelRange;
+	private int maximumPixelSize;
+	private int[] xBLength = new int[3];
+	private int[] yBLength = new int[3];
+	private int[] yBStart = new int[3];
+	private int[] yBEnd = new int[3];
+	private int externFrameCount;
+	private int internFrameCount;
+	private boolean restart;
+	
+	static {
+		int i;
+		
+		for(i = -CLIP_MIN; i < CLIP_MAX; i++) {
+			clip[i + CLIP_MIN] = (short) (i >= 0 ? i <= 255 ? i : 0xFF : 0);
+		}
+	}
+		
+	/**
+	 * Constructs an instance of <code>TheoraDecoder</code> with a
+	 * <code>VideoReader</code> object containing all necessary informations
+	 * about the media source.
+	 *
+	 * @param info     the <code>VideoReader</code> object containing all
+	 *      necessary informations about the media source
+	 */
+	TheoraDecoder(VideoReader info) {
+		super(info);
+		
+		nbs = info.blocks;
+		nsbs = info.superblocks;
+		nmbs = info.macroblocks;
+		sbChromaWidth = info.sbChromaWidth;
+		sbChromaHeight = info.sbChromaHeight;
+		sbLumaWidth = info.sbLumaWidth;
+		sbLumaHeight = info.sbLumaHeight;
+		mbWidth = info.mbWidth;
+		mbHeight = info.mbHeight;
+		qmat = info.qmat;
+		lflims = info.lflims;
+		maximumPixelSize = codedPictureWidth * codedPictureHeight;
+
+		chromaWidth = chromaFormat == CHROMA444 ? codedPictureWidth : codedPictureWidth >>> 1;
+		chromaHeight = chromaFormat != CHROMA420 ? codedPictureHeight : codedPictureHeight >>> 1;
+		
+		bChromaWidth = chromaWidth >>> 3;
+		bChromaHeight = chromaHeight >>> 3;
+		bLumaWidth = codedPictureWidth >>> 3;
+		bLumaHeight = codedPictureHeight >>> 3;
+		
+		nlsbs = sbLumaHeight * sbLumaWidth;
+		ncsbs = sbChromaHeight * sbChromaWidth;
+		
+		if (image == null) {
+			image = new short[3][];
+			image[0] = new short[maximumPixelSize];
+			image[1] = new short[chromaWidth * chromaHeight];
+			image[2] = new short[chromaWidth * chromaHeight];
+			ref  = new short[3][];
+			ref[0] = new short[maximumPixelSize];
+			ref[1] = new short[chromaWidth * chromaHeight];
+			ref[2] = new short[chromaWidth * chromaHeight];
+			goldRef = new short[3][];
+			goldRef[0] = new short[maximumPixelSize];
+			goldRef[1] = new short[chromaWidth * chromaHeight];
+			goldRef[2] = new short[chromaWidth * chromaHeight];
+		}
+		
+		zeroBytes = new byte[nsbs << 4];
+		oneBytes = new byte[nbs];
+		bCoded = new byte[nsbs][]; // Porting problem: Index can't be of type long (32 bit unsigned)
+	  	oneBytesBytes = new byte[nsbs][]; // Porting problem: Index can't be of type long (32 bit unsigned)
+	  	cbIndex = new int[nbs]; // Porting problem: Index can't be of type long (36 bit unsigned)
+		root = qcbIndex = new Node();
+		ncbIndex = new int[nbs]; // Porting problem: Index can't be of type long (36 bit unsigned)
+		cifbIndex = new int[nbs]; // Porting problem: Index can't be of type long (36 bit unsigned)
+		qiis = new byte[nsbs << 4]; // Porting problem: Index can't be of type long (36 bit unsigned)
+		tis = new byte[nsbs << 4]; // Porting problem: Index can't be of type long (36 bit unsigned)
+		coeffs = new short[nsbs << 4][64];
+		nCoeffs = new byte[nsbs << 4];
+		blockCoordinateX = new int[nsbs << 4];
+		blockCoordinateY = new int[nsbs << 4];
+		sbpCodedBits = new byte[nsbs]; // Porting problem: Should be long
+		sbfCodedBits = new byte[nsbs]; // Porting problem: Should be long
+		bits = new byte[nbs]; // Porting problem: Should be long
+
+		int blocksPerMakroblock = 4; // index 4 until 8 and 8 until 12 are equal to index 0 until 3
+		
+		if (chromaFormat == CHROMA420) {
+			blocksPerMakroblock = 5; // index 4 and 5 are equal
+		} else if (chromaFormat == CHROMA422) {
+			blocksPerMakroblock = 6; // index 4 and 5 are equal to index 6 and 7
+		}
+
+		mbMode = new byte[nmbs];
+		mbiToSbiLmbiMapping = new int[nmbs];
+		mVects = new byte[nmbs][blocksPerMakroblock][2];
+		biToMbiMapping = new int[nsbs << 4];
+		
+		int cpli, sbx, sby, sbi = 0, bi, sbibi, xSbEnd, ySbEnd, xBEnd, yBEnd, xEnd, yEnd;
+		int xSbLength[] = {sbLumaWidth, sbChromaWidth, sbChromaWidth};
+		int ySbLength[] = {sbLumaHeight, sbChromaHeight, sbChromaHeight};
+		
+		xBLength[0] = bLumaWidth;
+		xBLength[1] = xBLength[2] = bChromaWidth;
+		yBLength[0] = bLumaHeight;
+		yBLength[1] = yBLength[2] = bChromaHeight;
+		
+		int xSbRemainder;
+		int ySbRemainder;
+		int size;
+		int mbi;
+		int oneBytesIndex = 0;
+		
+		spipp = new int[nsbs];
+		pli = new int[nsbs];
+		planeWidthClip = new int[3][];
+		planeHeightClip = new int[3][];
+		
+		int[] planeWidthClipPointer = null;
+		int[] planeHeightClipPointer = null;
+		
+		int bx, by;
+
+		int mbWidth2 = mbWidth << 1;
+		int mbWidth4 = mbWidth << 2;
+		int ySbRemainderMinusOne;
+		int xSbRemainderMinusOne;
+		int ySbRemainderHalfMinusOne;
+		int ySbRemainder2;
+		int biShiftRightTwo;
+		
+		rasterOrderToCodedOrderMapping = new int[3][][];
+		
+		int[][] rasterOrderToCodedOrderMappingPointer;
+		
+		for (cpli = 0; cpli < 3; cpli++) {
+			xSbEnd = xSbLength[cpli];
+			ySbEnd = ySbLength[cpli];
+			xBEnd = xBLength[cpli];
+			yBEnd = yBLength[cpli];
+			xEnd = xBEnd << 3;
+			yEnd = yBEnd << 3;
+			rasterOrderToCodedOrderMappingPointer = rasterOrderToCodedOrderMapping[cpli] = new int[yBEnd][xBEnd];
+			if (cpli < 2) {
+				planeWidthClipPointer = planeWidthClip[cpli] = new int[xEnd + PLANE_CLIP_RANGE];
+				planeHeightClipPointer = planeHeightClip[cpli] = new int[yEnd + PLANE_CLIP_RANGE];
+				for (sbx = -PLANE_CLIP_MIN; sbx < xEnd + PLANE_CLIP_MAX; sbx++) {
+					planeWidthClipPointer[sbx + PLANE_CLIP_MIN] = sbx >= 0 ? sbx < xEnd ? sbx : xEnd - 1 : 0;
+				}
+				for (sby = -PLANE_CLIP_MIN; sby < yEnd + PLANE_CLIP_MAX; sby++) {
+					planeHeightClipPointer[sby + PLANE_CLIP_MIN] = sby >= 0 ? sby < yEnd ? sby : yEnd - 1 : 0;
+				}
+			} else {
+				planeWidthClip[2] = planeWidthClipPointer;
+				planeHeightClip[2] = planeHeightClipPointer;
+			}
+
+			for (sby = 0; sby < ySbEnd; sby++) {
+				if (sby == ySbEnd - 1) {
+					ySbRemainder = yBEnd & 0x3;
+					if (ySbRemainder == 0) {
+						ySbRemainder = 4;
+					}
+				} else {
+					ySbRemainder = 4;
+				}
+				ySbRemainderMinusOne = ySbRemainder - 1;
+				ySbRemainderHalfMinusOne = ySbRemainder / 2 - 1;
+				ySbRemainder2 = ySbRemainder << 1;
+				
+				for (sbx = 0; sbx < xSbEnd; sbx++) {
+					pli[sbi] = cpli;
+					if (sbx == xSbEnd - 1) {
+						xSbRemainder = xBEnd & 0x3;
+						if (xSbRemainder == 0) {
+							xSbRemainder = 4;
+						}
+					} else {
+						xSbRemainder = 4;
+					}
+					xSbRemainderMinusOne = xSbRemainder - 1;
+					size = xSbRemainder * ySbRemainder;
+
+					byte[] oneBytesBytesPointer = oneBytesBytes[sbi] = new byte[size];
+					
+					bCoded[sbi] = new byte[size];
+					
+					for (bi = 0; bi < size; bi++) {
+						oneBytesBytesPointer[bi] = 1;
+						oneBytes[oneBytesIndex++] = 1;
+						biShiftRightTwo = bi >>> 2;
+						sbibi = sbi << 4 | bi;
+						if (cpli == 0 || chromaFormat == CHROMA444) {
+							mbi = biToMbiMapping[sbibi] = biShiftRightTwo + sbx * ySbRemainder + sby * mbWidth2;
+							if (cpli == 0) {
+								mbiToSbiLmbiMapping[mbi] = sbi << 3 | biShiftRightTwo << 1 | ySbRemainder >>> 2;
+							}
+						} else {
+							if (chromaFormat == CHROMA422) {
+								biToMbiMapping[sbibi] = CHROMA_422_BI_TO_MBI_MAPPING[ySbRemainderHalfMinusOne][xSbRemainderMinusOne][bi] + sbx * ySbRemainder2 + sby * mbWidth2 - 1;
+							} else {
+								int value = CHROMA_420_BI_TO_MBI_MAPPING[ySbRemainderMinusOne][xSbRemainderMinusOne][bi];
+								
+								ySbRemainder2 = 8;
+
+								if (value < 0) {
+									value = -value;
+									if (ySbRemainder == 3) {
+										ySbRemainder2 = 4;
+									}
+									value += sbx * ySbRemainder2 + mbWidth2 + sby * mbWidth4 - 1;
+								} else {
+									if (ySbRemainder == 1) {
+										ySbRemainder2 = 4;
+									}
+									value += sbx * ySbRemainder2 + sby * mbWidth4 - 1;
+								}
+								biToMbiMapping[sbibi] = value;
+		 					}
+						}
+						cifbIndex[ncbs++] = sbibi;
+						qcbIndex = generate(sbibi, qcbIndex);
+						bx = sbx * 4 + SUPERBLOCK_X_BLOCKS_OFFSET[ySbRemainderMinusOne][xSbRemainderMinusOne][bi];
+						by = sby * 4 + SUPERBLOCK_Y_BLOCKS_OFFSET[ySbRemainderMinusOne][xSbRemainderMinusOne][bi];
+						rasterOrderToCodedOrderMappingPointer[by][bx] = sbibi;
+						blockCoordinateX[sbibi] = bx << 3;
+						blockCoordinateY[sbibi] = by << 3;
+					}
+					
+					if (cpli == 0) {
+						spipp[sbi] = sbi;
+					} else if (cpli == 1) {
+						spipp[sbi] = sbi - nlsbs;
+					} else {
+						spipp[sbi] = sbi - nlsbs - ncsbs;
+					}
+					sbi++;
+				}
+			}
+		}
+		
+		int qi; // The quantization index.
+		short r; // The edge detector response values.
+		short l; // The loop filter limit value.
+		short[] lflimPointer;
+
+		for (qi = 0; qi < 64; qi++) {
+			lflimPointer = lflim[qi];
+			l = lflims[qi];
+			for (r = 0; r < l; r++) {
+				lflimPointer[-r - l + FILTER_MIN] = (short) ( r - l);
+			 	lflimPointer[-r + FILTER_MIN] = (short) -r;
+			 	lflimPointer[ r + FILTER_MIN] = (short)  r;
+				lflimPointer[ r + l + FILTER_MIN] = (short) (-r + l);
+			}
+		}
+	}
+
+	/**
+	 * Decodes an still image.
+	 *
+	 * @exception IOException  if an I/O error occurs
+	 * @exception InterruptedIOException  if the decoding process is interrupted
+	 *      caused from malformed media data
+	 */
+	public synchronized void decodeStillImage() throws IOException {
+		skippedFrame = false;
+		try {
+			decode();
+			restart = true;
+		} catch (EndOfPacketException e) {
+			skippedFrame = true;
+		}
+	}
+
+	/**
+	 * Decodes an image.
+	 *
+	 * @exception IOException  if an I/O error occurs
+	 * @exception InterruptedIOException  if the decoding process is interrupted
+	 *      caused from malformed media data
+	 */
+	public synchronized void decodeImage() throws IOException {
+		skippedFrame = false;
+		try {
+			restart = false;
+			decode();
+		} catch (EndOfPacketException e) {
+			skippedFrame = true;
+		}
+	}
+	
+	void decode() throws IOException, EndOfPacketException {
+		
+		pixelOffset = maximumPixelSize;
+		
+		int pixelMax = -1;
+		
+		yBEnd[0] = yBEnd[1] = yBEnd[2] = 0;
+		yBStart[0] = yBLength[0];
+		yBStart[1] = yBStart[2] = yBLength[1];
+				
+		if (get1() != 0) {
+			throw new InterruptedIOException("No video packet");
+		}
+		
+		// 7.1 Frame Header Decode
+		
+		int moreQis; // A flag indicating there are more qi values to be decoded.
+		
+		int getBuffer = get(8);
+		
+		fType = getBuffer >>> 7;
+		qis[0] = getBuffer >>> 1 & 0x3F;
+		moreQis = getBuffer & 0x1;
+		
+		if (moreQis == 0) {
+			nqis = 1;
+		} else {
+			getBuffer = get(7);
+			
+			qis[1] = getBuffer >>> 1;
+			moreQis = getBuffer & 0x1;
+			if (moreQis == 0) {
+				nqis = 2;
+			} else {
+				qis[2] = get(6);
+				nqis = 3;
+			}
+		}
+		if (fType == INTRA_FRAME) {
+			pixelOffset = 0;
+			pixelRange = maximumPixelSize;
+			yBStart[0] = yBStart[1] = yBStart[2] = 0;
+			yBEnd[0] = yBLength[0];
+			yBEnd[1] = yBEnd[2] = yBLength[1];
+			if (get(3) != 0) {
+				throw new InterruptedIOException("Reserved values should be zero bytes");
+			}
+		}
+
+		// 7.3 Coded Block Flags Decode
+		// 7.4 Macro Block Coding Modes
+		
+		int nBits; // The length of a bit string to decode. // Porting problem: Should be long
+		int sbi; // The index of the current super block.
+		int bi; // The index of the current block in coded order.
+		int mbi = 0; // The index of the current macro block.
+		int lmbi = 0; // The index of the current luma macro block.
+		int psbi = -1; // The index of the previous super block.
+		int sbibi; // The index source of the index of the current super block and block.
+		
+		byte[] bCodedPointer;
+		byte[][] bCodedBuffer = bCoded;
+		byte[] mbModeBuffer = mbMode;
+		int[] cbIndexBuffer = cbIndex;
+		int bitCounter = 0; // Porting problem: Should be long
+		int biShiftRightTwo = 0;
+		byte mvx; // The horizontal component of the first whole-pixel motion vector.
+		byte mvy; // The vertical component of the first whole-pixel motion vector.
+		byte[][] mVectsPointer;
+		byte[] mVectsPointerArray;
+		byte mode;
+		
+		ncbs = 0;
+		nncbs = 0;
+		
+		qcbIndex = root;
+
+		if (fType == INTRA_FRAME) {
+			mbMode = oneBytes;
+			bCoded = oneBytesBytes;
+			cbIndex = cifbIndex;
+			ncbs = nbs;
+			qcbIndex = rebuildIntraCbIndexQueue(nbs, cbIndex, qcbIndex);
+		} else {
+			byte sbpCodedBit;
+			byte sbfCodedBit = 0;
+			int sbfCodedBitCounter = 0; // Porting problem: Should be long
+			int mScheme; // The mode coding scheme.
+			boolean[] lumaMakroBlockCoded = new boolean[4];
+			int lumaMakroBlockCodedValue;
+			
+			nBits = nsbs;
+			
+			runBitStringDecode(nBits, sbpCodedBits, HUFFMANCODETABLE7_7RSTART, HUFFMANCODETABLE7_7RBITS);
+			
+			for (sbi = nBits = 0; sbi < nsbs; sbi++) {
+				if (sbpCodedBits[sbi] == 0) {
+					nBits++;
+				}
+			}
+
+			if (nBits > 0) {
+
+				runBitStringDecode(nBits, sbfCodedBits, HUFFMANCODETABLE7_7RSTART, HUFFMANCODETABLE7_7RBITS);
+			
+				for (sbi = nBits = 0; sbi < nsbs; sbi++) {
+					if (sbpCodedBits[sbi] != 0) {
+						nBits += bCoded[sbi].length;
+					}
+				}
+			}
+
+			runBitStringDecode(nBits, bits, HUFFMANCODETABLE7_11RSTART, HUFFMANCODETABLE7_11RBITS);
+			
+			mScheme = get(3);
+			
+			if (mScheme == 0) {
+				for (mode = 0; mode < 8; mode++) {
+					// get(3) == The index of a Huffman code from Table 7.19, starting from 0
+					mAlphabet[get(3)] = mode;
+				}
+			} else if (mScheme != 7) {
+				System.arraycopy(HUFFMANCODETABLE7_14[mScheme], 0, mAlphabet, 0, mAlphabet.length);
+			}
+				
+			for (sbi = 0; sbi < nsbs; sbi++) {
+
+				bCodedPointer = bCoded[sbi];
+			
+				sbpCodedBit = sbpCodedBits[sbi];
+				
+				if (sbpCodedBit == 0) {
+					sbfCodedBit = sbfCodedBits[sbfCodedBitCounter++];
+				}
+	
+				lumaMakroBlockCoded[0] = false;
+				lumaMakroBlockCoded[1] = false;
+				lumaMakroBlockCoded[2] = false;
+				lumaMakroBlockCoded[3] = false;
+
+				for (bi = 0; bi < bCodedPointer.length; bi++) {
+
+					biShiftRightTwo = bi >>> 2;
+
+					lumaMakroBlockCodedValue = bCodedPointer[bi] = sbpCodedBit == 0 ? sbfCodedBit : bits[bitCounter++];
+					
+					if (lumaMakroBlockCodedValue != 0) {
+						sbibi = sbi << 4 | bi;
+						cbIndex[ncbs++] = sbibi;
+						qcbIndex = put(sbibi, qcbIndex);
+						lumaMakroBlockCoded[biShiftRightTwo] = true;
+					} else {
+						ncbIndex[nncbs++] = sbi << 4 | bi;
+					}
+				}
+				if (sbi < nlsbs) {
+					int lmbiEnd = biShiftRightTwo + 1;
+
+					for (lmbi = 0; lmbi < lmbiEnd; lmbi++, mbi++) {
+						mbMode[mbi] = lumaMakroBlockCoded[lmbi] ? mScheme != 7 ? mAlphabet[getHuffcodeTableIndex(mAlphabet.length)] : (byte) get(3) : INTER_NOMV;
+					}
+				}
+			}
+		
+			// 7.5 Motion Vectors
+			// 7.5.2 Macro Block Motion Vector Decode
+		
+			byte last1x = 0; // The last motion vector x value.
+			byte last1y = 0; // The last motion vector y value.
+			byte last2x = 0; // The last motion vector x value.
+			byte last2y = 0; // The last motion vector y value.
+			byte[] mVectsChroma1PointerArray;
+			byte[] mVectsChroma2PointerArray;
+			int mvMode = get1(); // The motion vector decoding method.
+			int mbModePointerValue;
+			int bri; // block index in raster order.
+			byte[] orderMappingPointer;
+			int sum, sumABx, sumABy, sumCDx, sumCDy;
+			byte mbModeValue = 0;
+			int mbiToSbiLmbiMappingValue;
+			int lmbiShiftLeftTwo;
+			
+			bCodedPointer = null;
+		
+			if (ncbs > 0) {
+				for (mbi = 0; mbi < mVects.length; mbi++) {
+					mbiToSbiLmbiMappingValue = mbiToSbiLmbiMapping[mbi];
+					sbi = mbiToSbiLmbiMappingValue >>> 3;
+					lmbi = mbiToSbiLmbiMappingValue >>> 1 & 0x3;
+					lmbiShiftLeftTwo = lmbi << 2;
+					mbModeValue = mbMode[mbi];
+					mVectsPointer = mVects[mbi];
+					orderMappingPointer = CODING_ORDER_TO_BLOCK_RASTER_ORDER[mbiToSbiLmbiMappingValue & 0x1][lmbi];
+					if (psbi != sbi) {
+						bCodedPointer = bCoded[sbi];
+						psbi = sbi;
+					}
+					mVectsPointerArray = mVectsPointer[0];
+					mVectsPointerArray[0] = mVectsPointerArray[1] = 0;
+			 		if (mbModeValue == INTER_MV_FOUR) {
+						last2x = last1x;
+						last2y = last1y;
+						sumABx = sumABy = sumCDx = sumCDy = 0;
+						for (bi = 0; bi < 4; bi++) {
+							bri = orderMappingPointer[bi];
+						
+							mVectsPointerArray = mVectsPointer[bri];
+						
+							if (bCodedPointer[lmbiShiftLeftTwo | bri] != 0) {
+								motionVectorDecode(mVectsPointerArray, mvMode);
+			 					last1x = mVectsPointerArray[0];
+								last1y = mVectsPointerArray[1];
+								if (bri < 2) {
+									sumABx += last1x;
+									sumABy += last1y;
+								} else {
+									sumCDx += last1x;
+									sumCDy += last1y;
+								}
+			 				} else {
+								mVectsPointerArray[0] = mVectsPointerArray[1] = 0;
+							}
+						}
+					
+						mVectsChroma1PointerArray = mVectsPointer[4];
+					
+						if (chromaFormat == CHROMA420) {
+							sum = sumABx + sumCDx;
+							mVectsChroma1PointerArray[0] = (byte) ((sum + (sum < 0 ? -2 : 2)) / 4);
+
+							sum = sumABy + sumCDy;
+							mVectsChroma1PointerArray[1] = (byte) ((sum + (sum < 0 ? -2 : 2)) / 4);
+						} else if (chromaFormat == CHROMA422) {
+							mVectsChroma2PointerArray = mVectsPointer[5];
+				
+							mVectsChroma1PointerArray[0] = (byte) ((sumABx + (sumABx < 0 ? -1 : 1)) / 2);
+							mVectsChroma2PointerArray[0] = (byte) ((sumCDx + (sumCDx < 0 ? -1 : 1)) / 2);
+							mVectsChroma1PointerArray[1] = (byte) ((sumABy + (sumABy < 0 ? -1 : 1)) / 2);
+							mVectsChroma2PointerArray[1] = (byte) ((sumCDy + (sumCDy < 0 ? -1 : 1)) / 2);
+						}
+					} else if (mbModeValue == INTER_GOLDEN_MV) {
+						motionVectorDecode(mVectsPointerArray, mvMode);
+					} else if (mbModeValue == INTER_MV_LAST2) {
+						mVectsPointerArray[0] = last2x;
+						mVectsPointerArray[1] = last2y;
+						last2x = last1x;
+						last2y = last1y;
+						last1x = mVectsPointerArray[0];
+						last1y = mVectsPointerArray[1];
+					} else if (mbModeValue == INTER_MV_LAST) {
+						mVectsPointerArray[0] = last1x;
+						mVectsPointerArray[1] = last1y;
+					} else if (mbModeValue == INTER_MV) {
+						motionVectorDecode(mVectsPointerArray, mvMode);
+						last2x = last1x;
+						last2y = last1y;
+						last1x = mVectsPointerArray[0];
+						last1y = mVectsPointerArray[1];
+					}
+					if (mbModeValue != INTER_MV_FOUR) {
+						mvx = mVectsPointerArray[0];
+						mvy = mVectsPointerArray[1];
+						for (bi = 1; bi < mVectsPointer.length; bi++) {
+							mVectsPointerArray = mVectsPointer[bi];
+							mVectsPointerArray[0] = mvx;
+							mVectsPointerArray[1] = mvy;
+						}
+					}
+				}
+			}
+		}
+		
+		// 7.6 Block-Level qi Decode
+		
+		if(ncbs > 0) {
+			int qii; // The index of qi value in the list of qi values defined for this frame.
+			int cbi; // The index of the current block in the coded block list.
+			int qiiPlusOne;
+		
+			nBits = ncbs;
+			System.arraycopy(zeroBytes, 0, qiis, 0, qiis.length);
+		
+			if (nqis > 1 && ncbs > 0) {
+			
+				runBitStringDecode(nBits, bits, HUFFMANCODETABLE7_7RSTART, HUFFMANCODETABLE7_7RBITS);
+			
+				for (qii = 0; ; qii++) {
+					qiiPlusOne = qii + 1;
+					for (cbi = bitCounter = nBits = 0; cbi < ncbs; cbi++) {
+						sbibi = cbIndex[cbi];
+						if (qiis[sbibi] == qii) {
+							qiis[sbibi] += bits[bitCounter++];
+							if (qiis[sbibi] == qiiPlusOne) {
+								nBits++;
+							}
+						}
+					}
+					if (qii < nqis - 2) {
+						runBitStringDecode(nBits, bits, HUFFMANCODETABLE7_7RSTART, HUFFMANCODETABLE7_7RBITS);
+					} else {
+						break;
+					}
+				}
+			}
+		
+      		// 7.7 DCT Coefficients
+			// 7.7.3 DCT Coefficient Decode
+		
+			int token; // No The current token being decoded.
+			int hg; // No The current Huffman table group.
+			int ti = 0; // The current token index.
+			int htil = 0; // The index of the current Huffman table to use for the luma plane within a group.
+			int htic = 0; // The  index of the current Huffman table to use for the chroma planes ithin a group.
+			int hti; // The index of the current Huffman table to use.
+			short[] coeffsPointer = null;
+			int psbibi = -1;
+		
+			System.arraycopy(zeroBytes, 0, tis, 0, tis.length);
+		
+			eobs = 0;
+		
+			for (; ti < 64 ; ti++) {
+				qcbIndex = root;
+		
+				if (ti == 0 || ti == 1) {
+					getBuffer = get(8);
+					htil = getBuffer >>> 4;
+					htic = getBuffer & 0xF;
+				}
+				
+				while (traversable(qcbIndex)) {
+			
+					qcbIndex = qcbIndex.next;
+				
+					sbibi = qcbIndex.index;
+					
+					if (psbibi != sbibi) {
+						coeffsPointer = coeffs[sbibi];
+						psbibi = sbibi;
+					}
+				
+					if (tis[sbibi] == ti) {
+						nCoeffs[sbibi] = (byte) ti;
+					
+						if (eobs != 0) {
+							coeffsPointer[ti] = 0;
+							tis[sbibi] = 64;
+							remove(qcbIndex);
+							eobs--;
+						} else {
+							hg = HUFFMAN_GROUP_TABLE7_42[ti];
+							hti = pli[sbibi >>> 4] == 0 ? hg << 4 | htil : hg << 4 | htic;
+						
+							token = getCodeWord(hti);
+						
+							if (token < 7) {
+								eobs = token < 3 ? token : get(TOKEN_TABLE7_33[0][token]) + TOKEN_TABLE7_33[1][token];
+								coeffsPointer[ti] = 0;
+								nCoeffs[sbibi] = tis[sbibi];
+								tis[sbibi] = 64;
+								remove(qcbIndex);
+							} else {
+								coefficientTokenDecode(coeffsPointer, nCoeffs, tis, token, sbibi, ti);
+							}
+						}
+					}
+				}
+			}
+		}
+		
+		// 7.8 Undoing DC Prediction
+		// 7.8.1 Computing the DC Predictor
+		// 7.8.2 Inverting the DC Prediction Process
+		// 7.9 Reconstruction
+		
+		int ci; // The DCT coefficient index in natural order
+				// in opposite to the DCT coefficient index in zig-zag order
+				// == {int zzi = ZIG_ZAG_SCAN[ci];}.
+		int dc = 0; // The actual DC value for the current block.
+		int dcPred = 0; // The predicted DC value for the current block.
+		int rfi; // The index of the reference frame indicated by the coding mode for macro block mbi.
+		int cpli = 0; // The current plane index.
+		int nbii; // The index of the index of neighbor blocks.
+		int nbiiCounter; // The index of the index of neighbor blocks counter.
+		int bj; // The index of a neighboring block in oded order.
+		int sbj; // The index of a neighboring superblock in oded order.
+		//int pdiv; // The valud to divide the weighted sum by.
+		short[] weightsDivisorTableOuterPointer;
+		int table47index;
+		//byte mvx; // The horizontal component of the first whole-pixel motion vector.
+		//byte mvy; // The vertical component of the first whole-pixel motion vector.
+		byte mvx2 = mvx = 0; // The horizontal component of the second whole-pixel motion vector.
+		byte mvy2 = mvy = 0; // The vertical component of the second whole-pixel motion vector.
+		int qti; // A quantization type index. See Table 3.1.
+		int qi0 = qis[0]; // The quantization index of the DC coefficient.
+		short[] refp = null; // A RPH/RPW array containing the contents of the current plane of the reference frame.
+		int refpi; // The pixel index of the reference plane pointer.
+		short[] imp = null; // A RPH/RPW array containing the contents of the current plane of the current frame.
+		int impi; // The pixel index of the current image plane pointer.
+		int[] clipW = null;
+		int[] clipH = null;
+		int[] rasterOrderToCodedOrderMappingInnerPointer;
+		int[][] rasterOrderToCodedOrderMappingOuterPointer;
+		int pbx; // The horizontal pixel index of the lower left edge of the current block.
+		int pby; // The vertical pixel index of the lower left edge of the current block.
+		int bx; // The horizontal block index of the current block.
+		int by; // The vertical block index of the current block.
+		int nbx; // The horizontal neighbor block index of the current block.
+		int nby; // The vertical neighbor block index of the current block.
+		int wcps = 0; // The width of the current plane in pixels.
+		int hcps = 0; // The height of the current plane in pixels.
+		int wcbs = 0; // The width of the current plane in blocks.
+		int hcbs; // The height of the current plane in blocks.
+		int sbjbj; // The index source of the index of the neighbor super block and block.
+		boolean sparseDc;
+		byte[] chromaSelectionPointer;
+		int nc;
+		int pbyShift = 0;
+		
+		for (; cpli < 3 && ncbs > 0; cpli++) {
+			System.arraycopy(ZERO_SHORTS, 0, lastDc, 0, 3);
+			clipW = planeWidthClip[cpli];
+			clipH = planeHeightClip[cpli];
+			wcps = clipW.length - PLANE_CLIP_RANGE;
+			hcps = clipH.length - PLANE_CLIP_RANGE;
+			wcbs = wcps >>> 3;
+			hcbs = hcps >>> 3;
+			imp = image[cpli];
+			rasterOrderToCodedOrderMappingOuterPointer = rasterOrderToCodedOrderMapping[cpli];
+			if (cpli != 0 && chromaFormat == CHROMA420) {
+				pbyShift = 1;
+			}
+			for (by = 0; by < hcbs; by++) {
+				pby = by << 3;
+				rasterOrderToCodedOrderMappingInnerPointer = rasterOrderToCodedOrderMappingOuterPointer[by];
+				chromaSelectionPointer = CHROMA_422_MOTIONVECTOR_SELECTION[by == hcbs - 1 && hcbs % 4 != 0 ? 0 : 1];
+
+				for (bx = 0; bx < wcbs; bx++) {
+					pbx = bx << 3;
+						
+					sbibi = rasterOrderToCodedOrderMappingInnerPointer[bx];
+					
+					sbi = sbibi >>> 4;
+					bi = sbibi & 0xF;
+					
+					mbi = biToMbiMapping[sbibi];
+
+					if (bCoded[sbi][bi] != 0) {
+						
+						mode = mbMode[mbi];
+
+						isNb[0] = bx != 0 ? true : false;
+						isNb[1] = bx != 0 && by != 0 ? true : false;
+						isNb[2] = by != 0 ? true : false;
+						isNb[3] = bx != wcbs - 1 && by != 0 ? true : false;
+					
+						rfi = CODE_MODE_TABLE7_46[mode];
+						
+						for (nbii = table47index = nbiiCounter = 0; nbii < 4; nbii++) {
+							if (isNb[nbii]) {
+								nbx = nbii == 0 ? bx - 1 : bx + nbii - 2;
+								nby = nbii == 0 ? by : by - 1;
+								sbjbj = rasterOrderToCodedOrderMappingOuterPointer[nby][nbx];
+					
+								sbj = sbjbj >>> 4;
+								bj = sbjbj & 0xF;
+							
+								if (bCoded[sbj][bj] != 0 && rfi == CODE_MODE_TABLE7_46[mbMode[biToMbiMapping[sbjbj]]]) {
+									table47index |= 1 << nbii;
+									coeffsValues[nbiiCounter++] = coeffs[sbjbj][0];
+								}
+							}
+						}
+						if (table47index == 0) {
+							coeffs[sbibi][0] += lastDc[rfi];
+						} else {
+							weightsDivisorTableOuterPointer = DCPREDICTORS_WEIGHTS_AND_DIVISORS_TABLE7_47[table47index];
+							dcPred = weightsDivisorTableOuterPointer[0] * coeffsValues[0];
+							if (nbiiCounter > 1) {
+								for (nbii = 1; nbii < nbiiCounter; nbii++) {
+									dcPred += weightsDivisorTableOuterPointer[nbii] * coeffsValues[nbii];
+								}
+								dcPred /= weightsDivisorTableOuterPointer[4];
+								if ((table47index & 0x7) == 7) {
+									if (dcPred - coeffsValues[2] > 128 || dcPred - coeffsValues[2] < -128) {
+										dcPred = coeffsValues[2];
+									} else if (dcPred - coeffsValues[0] > 128 || dcPred - coeffsValues[0] < -128) {
+										dcPred = coeffsValues[0];
+									} else if (dcPred - coeffsValues[1] > 128 || dcPred - coeffsValues[1] < -128) {
+										dcPred = coeffsValues[1];
+									}
+								}
+							}
+							coeffs[sbibi][0] += dcPred;
+						}
+						lastDc[rfi] = coeffs[sbibi][0];
+					
+						// 7.9.4 The Complete Reconstruction Algorithm
+					
+						qti = mode == INTRA ? 0 : 1;
+						
+						if (rfi != 0) {// Table 7.75: Reference Planes and Sizes for Each rfi and pli
+							if (rfi == 1) {
+								refp = ref[cpli];
+							} else {
+								refp = goldRef[cpli];
+							}
+							if (mode != INTER_NOMV && mode != INTER_GOLDEN_NOMV) {
+								mVectsPointer = mVects[mbi];
+				
+								if (cpli == 0 || chromaFormat == CHROMA444) {
+									mVectsPointerArray = mVectsPointer[bi & 0x3];
+								} else if (chromaFormat == CHROMA422) {
+									mVectsPointerArray = mVectsPointer[chromaSelectionPointer[bi]];
+								} else {
+									mVectsPointerArray = mVectsPointer[4];
+								}
+				
+								int xChromaDecimation = (chromaFormat & 1) == 0 && cpli != 0 ? 1 : 0;
+
+								int yChromaDecimation = (chromaFormat & 2) == 0 && cpli != 0 ? 1 : 0;
+				
+								mvx = MOTIONVECTOR_MAPPING_BASE[xChromaDecimation][mVectsPointerArray[0] + 31];
+								mvy = MOTIONVECTOR_MAPPING_BASE[yChromaDecimation][mVectsPointerArray[1] + 31];
+								mvx2 = MOTIONVECTOR_MAPPING_DIFF[xChromaDecimation][mVectsPointerArray[0] + 31];
+								mvy2 = MOTIONVECTOR_MAPPING_DIFF[yChromaDecimation][mVectsPointerArray[1] + 31];
+							} else {
+								mvx = 0;
+								mvy = 0;
+								mvx2 = 0;
+								mvy2 = 0;
+							}
+						}
+			
+						short[] qm0 = qmat[qti][cpli][qi0];
+						short[] qm = qmat[qti][cpli][qis[qiis[sbibi]]];
+
+						nc = nCoeffs[sbibi];
+				
+						sparseDc = false;
+
+						if (nc < 2) {
+							dc = (short) (coeffs[sbibi][0] * qm0[0] + 15 >> 5);
+							if (dc == 0) {
+								sparseDc = true;
+							} else {
+								for (ci = 0; ci < 64; ci++) {
+									dqc[ci] = dc;
+								}
+							}
+						} else {
+							dequantization(qm0, qm, coeffs[sbibi], nc);
+							if (nc <= 3) {
+								inverseDCT2D3();
+							} else if (nc <= 6) {
+								inverseDCT2D6();
+							} else if (nc <= 10) {
+								inverseDCT2D10();
+							} else {
+								inverseDCT2D(ZIG_ZAG_TO_NATURAL_ORDER_ROW_MAPPING[nc - 1]);
+							}
+						}
+						if (rfi == 0) {
+							intraPredictor(imp, clipW, pbx, pby, sparseDc);
+						} else {
+							if (mvx2 != 0 || mvy2 != 0) {
+								mvx2 += mvx;
+								mvy2 += mvy;
+								halfPixelPredictor(imp, refp, clipW, clipH, mvx, mvy, mvx2, mvy2, pbx, pby, sparseDc);
+							} else {
+								if (mvx != 0 || mvy != 0) {
+									wholePixelPredictor(imp, refp, clipW, clipH, mvx, mvy, pbx, pby, sparseDc);
+								} else {
+									wholePixelPredictor(imp, refp, clipW, clipH, pbx, pby, sparseDc);
+								}
+							}
+						}
+						impi = (pby << pbyShift) * codedPictureWidth;
+						
+						if (impi > pixelMax) {
+							pixelMax = impi;
+							pixelRange = pixelMax + 8 * codedPictureWidth;
+						}
+						if (impi < pixelOffset) {
+							pixelOffset = impi;
+							pixelRange = pixelMax + 8 * codedPictureWidth;
+						}
+					}
+					int byPlusOne = by + 1;
+					
+					if (byPlusOne > yBEnd[cpli]) {
+						yBEnd[cpli] = byPlusOne;
+					}
+					if (by < yBStart[cpli]) {
+						yBStart[cpli] = by;
+					}
+				}
+			}
+		}
+
+		short[][] swap; // swap image pointer.
+		int ppli = -1; // The last color plane index.
+		int pbi = 0; // The index of the current pointed block in the non coded or coded block list.
+		int pbs = ncbs; // An NBS-element array of pointed block indices // Porting problem: Should be long[]
+		int[] pbIndex = cbIndex; // An NBS-element array of pointed block indices // Porting problem: Should be long[]
+
+		if (ncbs > nncbs) {
+			swap = image;
+			image = ref;
+			ref = swap;
+			pbs = nncbs;
+			pbIndex = ncbIndex;
+		}
+
+		for (psbi = -1; pbi < pbs; pbi++) {
+			
+			sbibi = pbIndex[pbi];
+			
+			sbi = sbibi >>> 4;
+			
+			if (psbi != sbi) {
+				cpli = pli[sbi];
+				if (ppli != cpli) {
+					ppli = cpli;
+					imp = image[cpli];
+					refp = ref[cpli];
+					clipW = planeWidthClip[cpli];
+					clipH = planeHeightClip[cpli];
+					wcps = clipW.length - PLANE_CLIP_RANGE;
+					hcps = clipH.length - PLANE_CLIP_RANGE;
+				}
+				psbi = sbi;
+			}
+			
+			impi = blockCoordinateY[sbibi] * wcps + blockCoordinateX[sbibi];
+			System.arraycopy(imp, impi        , refp, impi, 8);
+			System.arraycopy(imp, impi += wcps, refp, impi, 8);
+			System.arraycopy(imp, impi += wcps, refp, impi, 8);
+			System.arraycopy(imp, impi += wcps, refp, impi, 8);
+			System.arraycopy(imp, impi += wcps, refp, impi, 8);
+			System.arraycopy(imp, impi += wcps, refp, impi, 8);
+			System.arraycopy(imp, impi += wcps, refp, impi, 8);
+			System.arraycopy(imp, impi += wcps, refp, impi, 8);
+		}
+		
+		// 7.10 Loop Filtering
+		// 7.10.3 Complete Loop Filter
+		
+		int fx; // The horizontal pixel index of the lower-left corner of the area to be filtered.
+		int fy; // The vertical pixel index of the lower-left corner of the area to be filtered.
+		int l = lflims[qi0]; // The loop filter limit value.
+		
+		if (l != 0) {
+			short[] lflimPointer = lflim[qi0];
+			int[] rasterOrderToCodedOrderMappingYPlusOne = null;
+			int bxPlusOne;
+			int byPlusOne;
+
+			for (cpli = 0; cpli < 3; cpli++) {
+				clipW = planeWidthClip[cpli];
+				clipH = planeHeightClip[cpli];
+				wcps = clipW.length - PLANE_CLIP_RANGE;
+				hcps = clipH.length - PLANE_CLIP_RANGE;
+				wcbs = wcps >>> 3;
+				hcbs = hcps >>> 3;
+				refp = ref[cpli];
+				rasterOrderToCodedOrderMappingOuterPointer = rasterOrderToCodedOrderMapping[cpli];
+
+				for (by = yBStart[cpli]; by < yBEnd[cpli]; by++) {
+					pby = by << 3;
+
+					rasterOrderToCodedOrderMappingInnerPointer = rasterOrderToCodedOrderMappingOuterPointer[by];
+					byPlusOne = by + 1;
+
+					if (byPlusOne < hcbs) {
+						rasterOrderToCodedOrderMappingYPlusOne = rasterOrderToCodedOrderMappingOuterPointer[byPlusOne];
+					}
+					for (bx = 0; bx < wcbs; bx++) {
+						pbx = bx << 3;
+
+						sbibi = rasterOrderToCodedOrderMappingInnerPointer[bx];
+					
+						sbi = sbibi >>> 4;
+						bi = sbibi & 0xF;
+
+						if(bCoded[sbi][bi] == 0) {
+							continue;
+						}
+					
+						bxPlusOne = bx + 1;
+						if (bx > 0) {
+							fx = pbx - 2;
+							fy = pby;
+							horizontalFilter(refp, lflimPointer, fx, fy, wcps);
+						}
+						if (by > 0) {
+							fx = pbx;
+							fy = pby - 2;
+							verticalFilter(refp, lflimPointer, fx, fy, wcps);
+						}
+						if (bxPlusOne < wcbs) {
+							sbjbj = rasterOrderToCodedOrderMappingInnerPointer[bxPlusOne];
+					
+							sbj = sbjbj >>> 4;
+							bj = sbjbj & 0xF;
+
+							if(bCoded[sbj][bj] == 0) {
+								fx = pbx + 6;
+								fy = pby;
+								horizontalFilter(refp, lflimPointer, fx, fy, wcps);
+							}
+						}
+						if (byPlusOne < hcbs) {
+							sbjbj = rasterOrderToCodedOrderMappingYPlusOne[bx];
+					
+							sbj = sbjbj >>> 4;
+							bj = sbjbj & 0xF;
+
+							if (bCoded[sbj][bj] == 0) {
+								fx = pbx;
+								fy = pby + 6;
+								verticalFilter(refp, lflimPointer, fx, fy, wcps);
+							}
+						}
+					}
+				}
+			}
+		}
+
+		if (fType == INTRA_FRAME) {
+			System.arraycopy(ref[0], 0, goldRef[0], 0, ref[0].length);
+			System.arraycopy(ref[1], 0, goldRef[1], 0, ref[1].length);
+			System.arraycopy(ref[2], 0, goldRef[2], 0, ref[2].length);
+			
+			mbMode = mbModeBuffer;
+			bCoded = bCodedBuffer;
+			cbIndex = cbIndexBuffer;
+		}
+		internFrameCount++;
+		internFrameCount %= maximumPixelSize;
+	}
+	
+	private void coefficientTokenDecode(short[] coeffs, byte[] nCoeffs, byte[] tis, int token, int bi, int ti) throws IOException, EndOfPacketException {
+		int sign; // A flag indicating the sign of the current coefficient.
+		int mag; // The magnitude of the current coefficient.
+		int rlen; // The length of the current ZERO_BYTES run.
+		int extraBits = TOKEN_TABLE7_33[0][token];
+		int offset = TOKEN_TABLE7_33[1][token];
+		int tokenRange = TOKEN_TABLE7_33[2][token];
+		int coeffsValue;
+		int coeffsIndex;
+		int getBuffer;
+		byte tisbi;
+		
+		if (tokenRange == 1) {
+			rlen = get(extraBits);
+			coeffsIndex = NATURAL_ORDER_SCAN[rlen + ti];
+			coeffsValue = 0;
+		} else if (tokenRange == 2) {
+			coeffsIndex = ti;
+			coeffsValue = offset;
+			
+			if (extraBits >= 1) {
+				sign = get1();
+				if (extraBits > 1) {
+					coeffsValue += get(extraBits - 1);
+				}
+				if (sign != 0) {
+					coeffsValue = -coeffsValue;
+				}
+			}
+		} else if (tokenRange == 3) {
+			coeffsValue = 1;
+			coeffsIndex = ti + offset;
+			sign = get1();
+			if (extraBits > 1) {
+				coeffsIndex += get(extraBits - 1);
+			}
+			if (sign != 0) {
+				coeffsValue = -coeffsValue;
+			}
+			coeffsIndex = NATURAL_ORDER_SCAN[coeffsIndex];
+		} else {
+			getBuffer = get(2);
+			sign = getBuffer >>> 1;
+			mag = (getBuffer & 0x1) + 2;
+			
+			if (offset == 0) {
+				coeffsIndex = NATURAL_ORDER_SCAN[ti + 1];
+			} else {
+				coeffsIndex = NATURAL_ORDER_SCAN[ti + get1() + 2];
+			}
+			coeffsValue = mag;
+			if (sign != 0) {
+				coeffsValue = -coeffsValue;
+			}
+		}
+		
+		rlen = coeffsIndex - ti;
+		
+		if (rlen > 0) {
+			System.arraycopy(ZERO_SHORTS, 0, coeffs, ti, rlen);
+		}
+		tisbi = tis[bi];
+		if ((tisbi += rlen + 1) >= 64) {
+			if (tisbi > 64) {
+				tisbi = 64;
+			}
+			qcbIndex = remove(qcbIndex);
+		}
+		tis[bi] = tisbi;
+		coeffs[coeffsIndex] = (short) coeffsValue;
+		if (tokenRange != 1) {
+			nCoeffs[bi] = tisbi;
+		}
+	}
+	
+	private void runBitStringDecode(int nBits, byte[] bits, byte[] huffmanCodeTableRstart, byte[] huffmanCodeTableRbits) throws IOException, EndOfPacketException {
+		
+		// 7.2 Run-Length Encoded Bit Strings
+		// 7.2.1 Long-Run Bit String Decode
+		// 7.2.2 Short-Run Bit String Decode
+		
+		int len = 0; // The number of bits decoded so far. // Porting problem: Should be long
+		int bit; // The value associated with the current run.
+		int rlen; // The length of the current run.
+		int rbits; // The number of extra bits needed to decode the run length.
+		//int rstart; // The start of the possible run-length values for a given Huffman code.
+		//int roffs; // The offset from RSTART of the runlength.
+		int index;
+		
+		if (len == nBits) {
+			return;
+		}
+
+		bit = get1();
+		
+		for (; ; ) {
+			index = getHuffcodeTableIndex(huffmanCodeTableRstart.length);
+			rbits = huffmanCodeTableRbits[index];
+			rlen = huffmanCodeTableRstart[index] + get(rbits);
+						
+			if (bit == 1) {
+				System.arraycopy(oneBytes, 0, bits, len, rlen);
+			} else {
+				System.arraycopy(zeroBytes, 0, bits, len, rlen);
+			}
+
+			len += rlen;
+			if (len >= nBits) {
+				return;
+			}
+			if (rlen == 4129) {
+				bit = get1();
+			} else {
+				bit ^= 1;
+			}
+		}
+	}
+	
+	private int getHuffcodeTableIndex(int length) throws IOException, EndOfPacketException {
+		int lengthMinusOne = length - 1;
+		int ilog = 0;
+		int i;
+		
+		for (i = 0; i < length; i++) {
+			if (get1() == 1) {
+				if (++ilog == lengthMinusOne) {
+					return ilog;
+				}
+			} else {
+				break;
+			}
+		}
+		return ilog;
+	}
+	
+	private void motionVectorDecode(byte[] mv, int mvMode) throws IOException, EndOfPacketException {
+		
+		// 7.5.1 Motion Vector Decode
+		
+		// int mvSign; // The sign of the motion vector component just decoded.
+		int bitPattern;
+		int mvx, mvy;
+		
+		if (mvMode == 0) {
+			mvx = HUFFMANCODETABLE7_23[0][get(3)];
+			if (mvx < 0) {
+				mvx = -mvx;
+				if (mvx == 2) {
+					bitPattern = get1();
+				} else {
+					bitPattern = get(mvx);
+				}
+				mvx = HUFFMANCODETABLE7_23[mvx][bitPattern];
+			}
+			mvx -= 31;
+			
+			mvy = HUFFMANCODETABLE7_23[0][get(3)];
+			if (mvy < 0) {
+				mvy = -mvy;
+				if (mvy == 2) {
+					bitPattern = get1();
+				} else {
+					bitPattern = get(mvy);
+				}
+				mvy = HUFFMANCODETABLE7_23[mvy][bitPattern];
+			}
+			mvy -= 31;
+			
+		} else {
+			mvx = get(5);
+			if (get1() == 1) {
+				mvx = -mvx;
+			}
+			mvy = get(5);
+			if (get1() == 1) {
+				mvy = -mvy;
+			}
+		}
+
+		mv[0] = (byte) mvx;
+		mv[1] = (byte) mvy;
+	}
+
+
+	private void intraPredictor(short[] image, int[] clipW, int pbx, int pby, boolean sparseDc) {
+
+		// 7.9.1 Predictors
+
+		int rpw = clipW.length - PLANE_CLIP_RANGE; // The width of the current plane of the reference frame in pixels.
+		int offset = pby * rpw + pbx;
+		
+		if (sparseDc) {
+			System.arraycopy(ONE_HUNDRED_TWENTYEIGHT_INTS, 0, image, offset       , 8);
+			System.arraycopy(ONE_HUNDRED_TWENTYEIGHT_INTS, 0, image, offset += rpw, 8);
+			System.arraycopy(ONE_HUNDRED_TWENTYEIGHT_INTS, 0, image, offset += rpw, 8);
+			System.arraycopy(ONE_HUNDRED_TWENTYEIGHT_INTS, 0, image, offset += rpw, 8);
+			System.arraycopy(ONE_HUNDRED_TWENTYEIGHT_INTS, 0, image, offset += rpw, 8);
+			System.arraycopy(ONE_HUNDRED_TWENTYEIGHT_INTS, 0, image, offset += rpw, 8);
+			System.arraycopy(ONE_HUNDRED_TWENTYEIGHT_INTS, 0, image, offset += rpw, 8);
+			System.arraycopy(ONE_HUNDRED_TWENTYEIGHT_INTS, 0, image, offset += rpw, 8);
+			return;
+		}
+		int j = 0;
+		int clipMinPlus128 = 128 + CLIP_MIN;
+
+		for (int i = 0; i < 8; i++) {
+			image[offset    ] = clip[dqc[j++] + clipMinPlus128];
+			image[offset | 1] = clip[dqc[j++] + clipMinPlus128];
+			image[offset | 2] = clip[dqc[j++] + clipMinPlus128];
+			image[offset | 3] = clip[dqc[j++] + clipMinPlus128];
+			image[offset | 4] = clip[dqc[j++] + clipMinPlus128];
+			image[offset | 5] = clip[dqc[j++] + clipMinPlus128];
+			image[offset | 6] = clip[dqc[j++] + clipMinPlus128];
+			image[offset | 7] = clip[dqc[j++] + clipMinPlus128];
+			offset += rpw;
+		}
+	}
+	
+	private void wholePixelPredictor(short[] image, short[] refp, int[] clipW, int[] clipH, int pbx, int pby, boolean sparseDc) {
+				
+		// 7.9.1 Predictors
+
+		int rpw = clipW.length - PLANE_CLIP_RANGE; // The width of the current plane of the reference frame in pixels.
+		int offset = pby * rpw + pbx;
+		
+		if (sparseDc) {
+			System.arraycopy(refp, offset       , image, offset, 8);
+			System.arraycopy(refp, offset += rpw, image, offset, 8);
+			System.arraycopy(refp, offset += rpw, image, offset, 8);
+			System.arraycopy(refp, offset += rpw, image, offset, 8);
+			System.arraycopy(refp, offset += rpw, image, offset, 8);
+			System.arraycopy(refp, offset += rpw, image, offset, 8);
+			System.arraycopy(refp, offset += rpw, image, offset, 8);
+			System.arraycopy(refp, offset += rpw, image, offset, 8);
+			return;
+		}
+		int j = 0;
+		
+		for (int i = 0; i < 8; i++) {
+			image[offset    ] = clip[dqc[j++] + refp[offset    ] + CLIP_MIN];
+			image[offset | 1] = clip[dqc[j++] + refp[offset | 1] + CLIP_MIN];
+			image[offset | 2] = clip[dqc[j++] + refp[offset | 2] + CLIP_MIN];
+			image[offset | 3] = clip[dqc[j++] + refp[offset | 3] + CLIP_MIN];
+			image[offset | 4] = clip[dqc[j++] + refp[offset | 4] + CLIP_MIN];
+			image[offset | 5] = clip[dqc[j++] + refp[offset | 5] + CLIP_MIN];
+			image[offset | 6] = clip[dqc[j++] + refp[offset | 6] + CLIP_MIN];
+			image[offset | 7] = clip[dqc[j++] + refp[offset | 7] + CLIP_MIN];
+			offset += rpw;
+		}
+	}
+
+	private void wholePixelPredictor(short[] image, short[] refp, int[] clipW, int[] clipH, int mvx, int mvy, int pbx, int pby, boolean sparseDc) {
+		
+		// 7.9.1 Predictors
+
+		int rpw = clipW.length - PLANE_CLIP_RANGE; // The width of the current plane of the reference frame in pixels.
+		int offset = pby * rpw + pbx;
+		int pbyPlusMvyPlusPlaneClipMin = pby + mvy + PLANE_CLIP_MIN;
+		int offsetXPlusPlaneClipMin = pbx + mvx + PLANE_CLIP_MIN;
+		int offsetY;
+		if (sparseDc) {
+			for (int i = 0; i < 8; i++) {
+				offsetY = clipH[pbyPlusMvyPlusPlaneClipMin + i] * rpw;
+				image[offset    ] = clip[refp[offsetY + clipW[offsetXPlusPlaneClipMin    ]] + CLIP_MIN];
+				image[offset | 1] = clip[refp[offsetY + clipW[offsetXPlusPlaneClipMin + 1]] + CLIP_MIN];
+				image[offset | 2] = clip[refp[offsetY + clipW[offsetXPlusPlaneClipMin + 2]] + CLIP_MIN];
+				image[offset | 3] = clip[refp[offsetY + clipW[offsetXPlusPlaneClipMin + 3]] + CLIP_MIN];
+				image[offset | 4] = clip[refp[offsetY + clipW[offsetXPlusPlaneClipMin + 4]] + CLIP_MIN];
+				image[offset | 5] = clip[refp[offsetY + clipW[offsetXPlusPlaneClipMin + 5]] + CLIP_MIN];
+				image[offset | 6] = clip[refp[offsetY + clipW[offsetXPlusPlaneClipMin + 6]] + CLIP_MIN];
+				image[offset | 7] = clip[refp[offsetY + clipW[offsetXPlusPlaneClipMin + 7]] + CLIP_MIN];
+				offset += rpw;
+			}
+			return;
+		}
+		int j = 0;
+		
+		for (int i = 0; i < 8; i++) {
+			offsetY = clipH[pbyPlusMvyPlusPlaneClipMin + i] * rpw;
+			image[offset    ] = clip[refp[offsetY + clipW[offsetXPlusPlaneClipMin    ]] + dqc[j++] + CLIP_MIN];
+			image[offset | 1] = clip[refp[offsetY + clipW[offsetXPlusPlaneClipMin + 1]] + dqc[j++] + CLIP_MIN];
+			image[offset | 2] = clip[refp[offsetY + clipW[offsetXPlusPlaneClipMin + 2]] + dqc[j++] + CLIP_MIN];
+			image[offset | 3] = clip[refp[offsetY + clipW[offsetXPlusPlaneClipMin + 3]] + dqc[j++] + CLIP_MIN];
+			image[offset | 4] = clip[refp[offsetY + clipW[offsetXPlusPlaneClipMin + 4]] + dqc[j++] + CLIP_MIN];
+			image[offset | 5] = clip[refp[offsetY + clipW[offsetXPlusPlaneClipMin + 5]] + dqc[j++] + CLIP_MIN];
+			image[offset | 6] = clip[refp[offsetY + clipW[offsetXPlusPlaneClipMin + 6]] + dqc[j++] + CLIP_MIN];
+			image[offset | 7] = clip[refp[offsetY + clipW[offsetXPlusPlaneClipMin + 7]] + dqc[j++] + CLIP_MIN];
+			offset += rpw;
+		}
+	}
+	
+	private void halfPixelPredictor(short[] image, short[] refp, int[] clipW, int[] clipH, int mvx, int mvy, int mvx2, int mvy2, int pbx, int pby, boolean sparseDc) {
+		
+		// 7.9.1 Predictors
+		
+		int rpw = clipW.length - PLANE_CLIP_RANGE; // The width of the current plane of the reference frame in pixels.
+		int offset = pby * rpw + pbx;
+		int pbyPlusMvyPlusPlaneClipMin = pby + mvy + PLANE_CLIP_MIN;
+		int pbyPlusMvy2PlusPlaneClipMin = pby + mvy2 + PLANE_CLIP_MIN;
+		int offsetXPlusPlaneClipMin = pbx + mvx + PLANE_CLIP_MIN;
+		int offsetX2PlusPlaneClipMin = pbx + mvx2 + PLANE_CLIP_MIN;
+		int offsetY;
+		int offsetY2;
+		if (sparseDc) {
+			for (int i = 0; i < 8; i++) {
+				offsetY = clipH[pbyPlusMvyPlusPlaneClipMin + i] * rpw;
+				offsetY2 = clipH[pbyPlusMvy2PlusPlaneClipMin + i] * rpw;
+				image[offset    ] = clip[(refp[offsetY + clipW[offsetXPlusPlaneClipMin    ]] + refp[offsetY2 + clipW[offsetX2PlusPlaneClipMin    ]] >> 1) + CLIP_MIN];
+				image[offset | 1] = clip[(refp[offsetY + clipW[offsetXPlusPlaneClipMin + 1]] + refp[offsetY2 + clipW[offsetX2PlusPlaneClipMin + 1]] >> 1) + CLIP_MIN];
+				image[offset | 2] = clip[(refp[offsetY + clipW[offsetXPlusPlaneClipMin + 2]] + refp[offsetY2 + clipW[offsetX2PlusPlaneClipMin + 2]] >> 1) + CLIP_MIN];
+				image[offset | 3] = clip[(refp[offsetY + clipW[offsetXPlusPlaneClipMin + 3]] + refp[offsetY2 + clipW[offsetX2PlusPlaneClipMin + 3]] >> 1) + CLIP_MIN];
+				image[offset | 4] = clip[(refp[offsetY + clipW[offsetXPlusPlaneClipMin + 4]] + refp[offsetY2 + clipW[offsetX2PlusPlaneClipMin + 4]] >> 1) + CLIP_MIN];
+				image[offset | 5] = clip[(refp[offsetY + clipW[offsetXPlusPlaneClipMin + 5]] + refp[offsetY2 + clipW[offsetX2PlusPlaneClipMin + 5]] >> 1) + CLIP_MIN];
+				image[offset | 6] = clip[(refp[offsetY + clipW[offsetXPlusPlaneClipMin + 6]] + refp[offsetY2 + clipW[offsetX2PlusPlaneClipMin + 6]] >> 1) + CLIP_MIN];
+				image[offset | 7] = clip[(refp[offsetY + clipW[offsetXPlusPlaneClipMin + 7]] + refp[offsetY2 + clipW[offsetX2PlusPlaneClipMin + 7]] >> 1) + CLIP_MIN];
+				offset += rpw;
+			}
+			return;
+		}
+		int j = 0;
+
+		for (int i = 0; i < 8; i++) {
+			offsetY = clipH[pbyPlusMvyPlusPlaneClipMin + i] * rpw;
+			offsetY2 = clipH[pbyPlusMvy2PlusPlaneClipMin + i] * rpw;
+			image[offset    ] = clip[(refp[offsetY + clipW[offsetXPlusPlaneClipMin    ]] + refp[offsetY2 + clipW[offsetX2PlusPlaneClipMin    ]] >> 1) + dqc[j++] + CLIP_MIN];
+			image[offset | 1] = clip[(refp[offsetY + clipW[offsetXPlusPlaneClipMin + 1]] + refp[offsetY2 + clipW[offsetX2PlusPlaneClipMin + 1]] >> 1) + dqc[j++] + CLIP_MIN];
+			image[offset | 2] = clip[(refp[offsetY + clipW[offsetXPlusPlaneClipMin + 2]] + refp[offsetY2 + clipW[offsetX2PlusPlaneClipMin + 2]] >> 1) + dqc[j++] + CLIP_MIN];
+			image[offset | 3] = clip[(refp[offsetY + clipW[offsetXPlusPlaneClipMin + 3]] + refp[offsetY2 + clipW[offsetX2PlusPlaneClipMin + 3]] >> 1) + dqc[j++] + CLIP_MIN];
+			image[offset | 4] = clip[(refp[offsetY + clipW[offsetXPlusPlaneClipMin + 4]] + refp[offsetY2 + clipW[offsetX2PlusPlaneClipMin + 4]] >> 1) + dqc[j++] + CLIP_MIN];
+			image[offset | 5] = clip[(refp[offsetY + clipW[offsetXPlusPlaneClipMin + 5]] + refp[offsetY2 + clipW[offsetX2PlusPlaneClipMin + 5]] >> 1) + dqc[j++] + CLIP_MIN];
+			image[offset | 6] = clip[(refp[offsetY + clipW[offsetXPlusPlaneClipMin + 6]] + refp[offsetY2 + clipW[offsetX2PlusPlaneClipMin + 6]] >> 1) + dqc[j++] + CLIP_MIN];
+			image[offset | 7] = clip[(refp[offsetY + clipW[offsetXPlusPlaneClipMin + 7]] + refp[offsetY2 + clipW[offsetX2PlusPlaneClipMin + 7]] >> 1) + dqc[j++] + CLIP_MIN];
+			offset += rpw;
+		}
+
+	}
+	
+	private void dequantization(short[] qmat0, short[] qmat, short[] coeffs, int nCoeffs) {
+		
+		// 7.9.2 Dequantization
+		
+		int zzi = 1; // The DCT coefficient index in zig-zag order
+				 // in opposite to the DCT coefficient index in natural order
+				 // == {int ci = ZIG_ZAG_SCAN[zzi];}.
+		
+		dqc[0] = (short) (coeffs[0] * qmat0[0]);
+		
+		for (; zzi < nCoeffs; zzi++) {
+			dqc[INVERSE_ZIG_ZAG_SCAN[zzi]] = (short) (coeffs[zzi] * qmat[zzi]);
+		}
+		for (; zzi < 64; zzi++) {
+			dqc[INVERSE_ZIG_ZAG_SCAN[zzi]] = 0;
+		}
+	}
+	
+	private void inverseDCT1D4Row(int shift) {
+		
+		// 7.9.3 The Inverse DCT
+		
+		// int t0, t1, t2, t3, t4, t5, t6, t7; // An 8-element array containing the current value of each signal line.
+		// int r; // A temporary value.
+		
+		int t0 = C4 * y0 >> 16;
+		int t2 = C6 * y2 >> 16;
+		int t3 = S6 * y2 >> 16;
+		int t4 = C7 * y1 >> 16;
+		int t5 = -(S3 * y3 >> 16);
+		int t6 = C3 * y3 >> 16;
+		int t7 = S7 * y1 >> 16;
+		int r = t4 + t5;
+		t5 = (short) (t4 - t5);
+		t5 = C4 * t5 >> 16;
+		t4 = r;
+		r = t7 + t6;
+		t6 = (short) (t7 - t6);
+		t6 = C4 * t6 >> 16;
+		t7 = r;
+		int t1 = t0 + t2;
+		t2 = t0 - t2;
+		r = t0 + t3;
+		t3 = t0 - t3;
+		t0 = r;
+		r = t6 + t5;
+		t5 = t6 - t5;
+		t6 = r;
+		dqc[shift    ] = (short) (t0 + t7);
+		dqc[shift | 1] = (short) (t1 + t6);
+		dqc[shift | 2] = (short) (t2 + t5);
+		dqc[shift | 3] = (short) (t3 + t4);
+		dqc[shift | 4] = (short) (t3 - t4);
+		dqc[shift | 5] = (short) (t2 - t5);
+		dqc[shift | 6] = (short) (t1 - t6);
+		dqc[shift | 7] = (short) (t0 - t7);
+	}
+	
+	private void inverseDCT1D3Row(int shift) {
+		
+		// 7.9.3 The Inverse DCT
+		
+		// int t0, t1, t2, t3, t4, t5, t6, t7; // An 8-element array containing the current value of each signal line.
+		// int r; // A temporary value.
+		
+		int t0 = C4 * y0 >> 16;
+		int t2 = C6 * y2 >> 16;
+		int t3 = S6 * y2 >> 16;
+		int t4 = C7 * y1 >> 16;
+		int t7 = S7 * y1 >> 16;
+		int t5 = C4 * t4 >> 16;
+		int t6 = C4 * t7 >> 16;
+		int t1 = t0 + t2;
+		t2 = t0 - t2;
+		int r = t0 + t3;
+		t3 = t0 - t3;
+		t0 = r;
+		r = t6 + t5;
+		t5 = t6 - t5;
+		t6 = r;
+		dqc[shift    ] = (short) (t0 + t7);
+		dqc[shift | 1] = (short) (t1 + t6);
+		dqc[shift | 2] = (short) (t2 + t5);
+		dqc[shift | 3] = (short) (t3 + t4);
+		dqc[shift | 4] = (short) (t3 - t4);
+		dqc[shift | 5] = (short) (t2 - t5);
+		dqc[shift | 6] = (short) (t1 - t6);
+		dqc[shift | 7] = (short) (t0 - t7);
+	}
+	
+	private void inverseDCT1D2Row(int shift) {
+		
+		// 7.9.3 The Inverse DCT
+		
+		// int t0, t4, t5, t6, t7; // An 8-element array containing the current value of each signal line.
+		// int r; // A temporary value.
+		
+		int t0 = C4 * y0 >> 16;
+		int t4 = C7 * y1 >> 16;
+		int t7 = S7 * y1 >> 16;
+		int t5 = C4 * t4 >> 16;
+		int t6 = C4 * t7 >> 16;
+		int r = t6 + t5;
+		t5 = t6 - t5;
+		t6 = r;
+		dqc[shift    ] = (short) (t0 + t7);
+		dqc[shift | 1] = (short) (t0 + t6);
+		dqc[shift | 2] = (short) (t0 + t5);
+		dqc[shift | 3] = (short) (t0 + t4);
+		dqc[shift | 4] = (short) (t0 - t4);
+		dqc[shift | 5] = (short) (t0 - t5);
+		dqc[shift | 6] = (short) (t0 - t6);
+		dqc[shift | 7] = (short) (t0 - t7);
+	}
+
+	private void inverseDCT1D1Row(int shift) {
+		
+		// 7.9.3 The Inverse DCT
+		
+		int t0 = C4 * y0 >> 16;
+		
+		dqc[shift    ] = t0;
+		dqc[shift | 1] = t0;
+		dqc[shift | 2] = t0;
+		dqc[shift | 3] = t0;
+		dqc[shift | 4] = t0;
+		dqc[shift | 5] = t0;
+		dqc[shift | 6] = t0;
+		dqc[shift | 7] = t0;
+	}
+	
+	private void inverseDCT2D(int maxRow) {
+		
+		// 7.9.3 The Inverse DCT
+		
+		int ci; // The column index.
+		int ri; // The row index.
+		int shift;
+		int y0, y1, y2, y3, y4, y5, y6, y7;
+		int t0, t1, t2, t3, t4, t5, t6, t7; // An 8-element array containing the current value of each signal line.
+		int r; // A temporary value.
+		
+		for (ri = 0; ri <= maxRow; ri++) {
+			shift = ri << 3;
+			y0 = dqc[shift    ];
+			y1 = dqc[shift | 1];
+			y2 = dqc[shift | 2];
+			y3 = dqc[shift | 3];
+			y4 = dqc[shift | 4];
+			y5 = dqc[shift | 5];
+			y6 = dqc[shift | 6];
+			y7 = dqc[shift | 7];
+			t0 = (short) (y0 + y4);
+			t0 = C4 * t0 >> 16;
+			t1 = (short) (y0 - y4);
+			t1 = C4 * t1 >> 16;
+			t2 = (C6 * y2 >> 16) - (S6 * y6 >> 16);
+			t3 = (S6 * y2 >> 16) + (C6 * y6 >> 16);
+			t4 = (C7 * y1 >> 16) - (S7 * y7 >> 16);
+			t5 = (C3 * y5 >> 16) - (S3 * y3 >> 16);
+			t6 = (S3 * y5 >> 16) + (C3 * y3 >> 16);
+			t7 = (S7 * y1 >> 16) + (C7 * y7 >> 16);
+			r = t4 + t5;
+			t5 = (short) (t4 - t5);
+			t5 = C4 * t5 >> 16;
+			t4 = r;
+			r = t7 + t6;
+			t6 = (short) (t7 - t6);
+			t6 = C4 * t6 >> 16;
+			t7 = r;
+			r = t0 + t3;
+			t3 = t0 - t3;
+			t0 = r;
+			r = t1 + t2;
+			t2 = t1 - t2;
+			t1 = r;
+			r = t6 + t5;
+			t5 = t6 - t5;
+			t6 = r;
+			dqc[shift    ] = (short) (t0 + t7);
+			dqc[shift | 1] = (short) (t1 + t6);
+			dqc[shift | 2] = (short) (t2 + t5);
+			dqc[shift | 3] = (short) (t3 + t4);
+			dqc[shift | 4] = (short) (t3 - t4);
+			dqc[shift | 5] = (short) (t2 - t5);
+			dqc[shift | 6] = (short) (t1 - t6);
+			dqc[shift | 7] = (short) (t0 - t7);
+		}
+		for (ci = 0; ci < 8; ci++) {
+			y0 = dqc[ci     ];
+			y1 = dqc[ 8 | ci];
+			y2 = dqc[16 | ci];
+			y3 = dqc[24 | ci];
+			y4 = dqc[32 | ci];
+			y5 = dqc[40 | ci];
+			y6 = dqc[48 | ci];
+			y7 = dqc[56 | ci];
+			t0 = (short) (y0 + y4);
+			t0 = C4 * t0 >> 16;
+			t1 = (short) (y0 - y4);
+			t1 = C4 * t1 >> 16;
+			t2 = (C6 * y2 >> 16) - (S6 * y6 >> 16);
+			t3 = (S6 * y2 >> 16) + (C6 * y6 >> 16);
+			t4 = (C7 * y1 >> 16) - (S7 * y7 >> 16);
+			t5 = (C3 * y5 >> 16) - (S3 * y3 >> 16);
+			t6 = (S3 * y5 >> 16) + (C3 * y3 >> 16);
+			t7 = (S7 * y1 >> 16) + (C7 * y7 >> 16);
+			r = t4 + t5;
+			t5 = (short) (t4 - t5);
+			t5 = C4 * t5 >> 16;
+			t4 = r;
+			r = t7 + t6;
+			t6 = (short) (t7 - t6);
+			t6 = C4 * t6 >> 16;
+			t7 = r;
+			r = t0 + t3;
+			t3 = t0 - t3;
+			t0 = r;
+			r = t1 + t2;
+			t2 = t1 - t2;
+			t1 = r;
+			r = t6 + t5;
+			t5 = t6 - t5;
+			t6 = r;
+			dqc[     ci] = (short) (t0 + t7) + 8 >> 4;
+			dqc[8  | ci] = (short) (t1 + t6) + 8 >> 4;
+			dqc[16 | ci] = (short) (t2 + t5) + 8 >> 4;
+			dqc[24 | ci] = (short) (t3 + t4) + 8 >> 4;
+			dqc[32 | ci] = (short) (t3 - t4) + 8 >> 4;
+			dqc[40 | ci] = (short) (t2 - t5) + 8 >> 4;
+			dqc[48 | ci] = (short) (t1 - t6) + 8 >> 4;
+			dqc[56 | ci] = (short) (t0 - t7) + 8 >> 4;
+		}
+	}
+	
+	private void inverseDCT2D3() {
+		
+		// 7.9.3 The Inverse DCT
+		
+		int ci; // The column index.
+		int ri; // The row index.
+		int shift = 0;
+		int t0, t4, t5, t6, t7; // An 8-element array containing the current value of each signal line.
+		int r; // A temporary value.
+			
+		y0 = dqc[0];
+		y1 = dqc[1];
+		inverseDCT1D2Row(shift);
+		shift = 8;
+		y0 = dqc[shift    ];
+		inverseDCT1D1Row(shift);
+		
+		for (ci = 0; ci < 8; ci++) {
+			int y0 = dqc[     ci];
+			int y1 = dqc[ 8 | ci];
+			t0 = C4 * y0 >> 16;
+			t4 = C7 * y1 >> 16;
+			t7 = S7 * y1 >> 16;
+			t5 = C4 * t4 >> 16;
+			t6 = C4 * t7 >> 16;
+			r = t6 + t5;
+			t5 = t6 - t5;
+			t6 = r;
+			dqc[     ci] = (short) (t0 + t7) + 8 >> 4;
+			dqc[8  | ci] = (short) (t0 + t6) + 8 >> 4;
+			dqc[16 | ci] = (short) (t0 + t5) + 8 >> 4;
+			dqc[24 | ci] = (short) (t0 + t4) + 8 >> 4;
+			dqc[32 | ci] = (short) (t0 - t4) + 8 >> 4;
+			dqc[40 | ci] = (short) (t0 - t5) + 8 >> 4;
+			dqc[48 | ci] = (short) (t0 - t6) + 8 >> 4;
+			dqc[56 | ci] = (short) (t0 - t7) + 8 >> 4;
+		}
+	}
+
+	private void inverseDCT2D6() {
+		
+		// 7.9.3 The Inverse DCT
+		
+		int ci; // The column index.
+		int ri; // The row index.
+		int shift = 0;
+		int t0, t1, t2, t3, t4, t5, t6, t7; // An 8-element array containing the current value of each signal line.
+		int r; // A temporary value.
+		
+		y0 = dqc[0];
+		y1 = dqc[1];
+		y2 = dqc[2];
+		inverseDCT1D3Row(shift);
+		shift = 8;
+		y0 = dqc[shift    ];
+		y1 = dqc[shift | 1];
+		inverseDCT1D2Row(shift);
+		shift = 16;
+		y0 = dqc[shift    ];
+		inverseDCT1D1Row(shift);
+		
+		for (ci = 0; ci < 8; ci++) {
+			int y1 = dqc[ 8 | ci];
+			int y2 = dqc[16 | ci];
+			t0 = C4 * dqc[ci] >> 16;
+			t2 = C6 * y2 >> 16;
+			t3 = S6 * y2 >> 16;
+			t4 = C7 * y1 >> 16;
+			t7 = S7 * y1 >> 16;
+			t5 = C4 * t4 >> 16;
+			t6 = C4 * t7 >> 16;
+			t1 = t0 + t2;
+			t2 = t0 - t2;
+			r = t0 + t3;
+			t3 = t0 - t3;
+			t0 = r;
+			r = t6 + t5;
+			t5 = t6 - t5;
+			t6 = r;
+			dqc[     ci] = (short) (t0 + t7) + 8 >> 4;
+			dqc[8  | ci] = (short) (t1 + t6) + 8 >> 4;
+			dqc[16 | ci] = (short) (t2 + t5) + 8 >> 4;
+			dqc[24 | ci] = (short) (t3 + t4) + 8 >> 4;
+			dqc[32 | ci] = (short) (t3 - t4) + 8 >> 4;
+			dqc[40 | ci] = (short) (t2 - t5) + 8 >> 4;
+			dqc[48 | ci] = (short) (t1 - t6) + 8 >> 4;
+			dqc[56 | ci] = (short) (t0 - t7) + 8 >> 4;
+		}
+	}
+
+	private void inverseDCT2D10() {
+		
+		// 7.9.3 The Inverse DCT
+		
+		int ci; // The column index.
+		int ri; // The row index.
+		int shift = 0;
+		int t0, t1, t2, t3, t4, t5, t6, t7; // An 8-element array containing the current value of each signal line.
+		int r; // A temporary value.
+
+		y0 = dqc[0];
+		y1 = dqc[1];
+		y2 = dqc[2];
+		y3 = dqc[3];
+		inverseDCT1D4Row(shift);
+		shift = 8;
+		y0 = dqc[shift    ];
+		y1 = dqc[shift | 1];
+		y2 = dqc[shift | 2];
+		inverseDCT1D3Row(shift);
+		shift = 16;
+		y0 = dqc[shift    ];
+		y1 = dqc[shift | 1];
+		inverseDCT1D2Row(shift);
+		shift = 24;
+		y0 = dqc[shift    ];
+		inverseDCT1D1Row(shift);
+		
+		for (ci = 0; ci < 8; ci++) {
+			int y1 = dqc[ 8 | ci];
+			int y2 = dqc[16 | ci];
+			int y3 = dqc[24 | ci];
+			t0 = C4 * dqc[ci] >> 16;
+			t2 = C6 * y2 >> 16;
+			t3 = S6 * y2 >> 16;
+			t4 = C7 * y1 >> 16;
+			t5 = -(S3 * y3 >> 16);
+			t6 = C3 * y3 >> 16;
+			t7 = S7 * y1 >> 16;
+			r = t4 + t5;
+			t5 = (short) (t4 - t5);
+			t5 = C4 * t5 >> 16;
+			t4 = r;
+			r = t7 + t6;
+			t6 = (short) (t7 - t6);
+			t6 = C4 * t6 >> 16;
+			t7 = r;
+			t1 = t0 + t2;
+			t2 = t0 - t2;
+			r = t0 + t3;
+			t3 = t0 - t3;
+			t0 = r;
+			r = t6 + t5;
+			t5 = t6 - t5;
+			t6 = r;
+			dqc[     ci] = (short) (t0 + t7) + 8 >> 4;
+			dqc[8  | ci] = (short) (t1 + t6) + 8 >> 4;
+			dqc[16 | ci] = (short) (t2 + t5) + 8 >> 4;
+			dqc[24 | ci] = (short) (t3 + t4) + 8 >> 4;
+			dqc[32 | ci] = (short) (t3 - t4) + 8 >> 4;
+			dqc[40 | ci] = (short) (t2 - t5) + 8 >> 4;
+			dqc[48 | ci] = (short) (t1 - t6) + 8 >> 4;
+			dqc[56 | ci] = (short) (t0 - t7) + 8 >> 4;
+		}
+	}
+	
+	private void horizontalFilter(short[] recp, short[] lflim, int fx, int fy, int rpw){
+		
+		// 7.10.1 Horizontal Filter
+		
+		int r; // The edge detector response.
+		int by; // The vertical pixel index in the block.
+		int pi = fy * rpw + fx; // Pixelindex.
+		int piPlus1 = pi + 1;
+		int piPlus2 = pi + 2;
+		
+		for (by = 0; by < 8; by++) {
+			r = recp[pi] - 3 * recp[piPlus1] + 3 * recp[piPlus2] - recp[pi + 3] + 4 >> 3;
+			recp[piPlus1] = clip[recp[piPlus1] + lflim[r + FILTER_MIN] + CLIP_MIN];
+			recp[piPlus2] = clip[recp[piPlus2] - lflim[r + FILTER_MIN] + CLIP_MIN];
+			pi += rpw;
+			piPlus1 = pi + 1;
+			piPlus2 = pi + 2;
+		}
+	}
+	
+	private void verticalFilter(short[] recp, short[] lflim, int fx, int fy, int rpw){
+		
+		// 7.10.2 Vertical Filter
+		
+		int r; // The edge detector response.
+		int bx; // The horizontal pixel index in the block.
+		int pi = fy * rpw + fx; // Pixelcoordinate.
+		int piPlusRpw = pi + rpw;
+		int piPlus2Rpw = piPlusRpw + rpw;
+		int piPlus3Rpw = piPlus2Rpw + rpw;
+		int piPlusRpwPlusBx;
+		int piPlus2RpwPlusBx;
+		
+		for (bx = 0; bx < 8; bx++) {
+			piPlusRpwPlusBx = piPlusRpw + bx;
+			piPlus2RpwPlusBx = piPlus2Rpw + bx;
+			r = recp[pi + bx] - 3 * recp[piPlusRpwPlusBx] + 3 * recp[piPlus2RpwPlusBx] - recp[piPlus3Rpw + bx] + 4 >> 3;
+			recp[piPlusRpwPlusBx] = clip[recp[piPlusRpwPlusBx] + lflim[r + FILTER_MIN] + CLIP_MIN];
+			recp[piPlus2RpwPlusBx] = clip[recp[piPlus2RpwPlusBx] - lflim[r + FILTER_MIN] + CLIP_MIN];
+		}
+	}
+
+	boolean display() {
+		if (ncbs == 0 || externFrameCount == internFrameCount) {
+			return false;
+		}
+
+		py = ref[0]; // Y
+		pv = ref[1]; // Cb
+		pu = ref[2]; // Cr
+		
+		if (internFrameCount == externFrameCount + 1 && !restart) {
+			super.display(pixelOffset, pixelRange);
+		} else {
+			yBStart[0] = yBStart[1] = yBStart[2] = 0;
+			yBEnd[0] = yBLength[0];
+			yBEnd[1] = yBEnd[2] = yBLength[1];
+			super.display(0, maximumPixelSize);
+		}
+		externFrameCount = internFrameCount;
+		return true;
+	}
+
+	
+	/**
+	 * Frees all system resources, which are bounded to this object.
+	 */
+	public void close() {
+		super.close();
+		int i, j, k;
+		
+		if (image == null) {
+			return;
+		}
+		image[0] = null;
+		image[1] = null;
+		image[2] = null;
+		image = null;
+		ref[0] = null;
+		ref[1] = null;
+		ref[2] = null;
+		ref = null;
+		goldRef[0] = null;
+		goldRef[1] = null;
+		goldRef[2] = null;
+		goldRef = null;
+		qis = null;
+		for (i = 0; i < bCoded.length; i++) {
+			bCoded[i] = null;
+		}
+		bCoded = null;
+		for (i = 0; i < oneBytesBytes.length; i++) {
+			oneBytesBytes[i] = null;
+		}
+		oneBytesBytes = null;
+		cbIndex = null;
+		ncbIndex = null;
+		cifbIndex = null;
+		qiis = null;
+		mbMode = null;
+		mbiToSbiLmbiMapping = null;
+		for (i = 0; i < lflim.length; i++) {
+			lflim[i] = null;
+		}
+		lflim = null;
+		nCoeffs = null;
+		tis = null;
+		
+		byte[][] mVectsPointer;
+	
+		for (i = 0; i < mVects.length; i++) {
+			mVectsPointer = mVects[i];
+			for (j = 0; j < mVectsPointer.length; j++) {
+				mVectsPointer[j] = null;
+			}
+			mVectsPointer = null;
+		}
+		mVects = null;
+		
+		int[][] rasterOrderToCodedOrderMappingPointer;
+		
+		for (i = 0; i < rasterOrderToCodedOrderMapping.length; i++) {
+			rasterOrderToCodedOrderMappingPointer = rasterOrderToCodedOrderMapping[i];
+			for (j = 0; j < rasterOrderToCodedOrderMappingPointer.length; j++) {
+				rasterOrderToCodedOrderMappingPointer[j] = null;
+			}
+			rasterOrderToCodedOrderMappingPointer = null;
+		}
+		rasterOrderToCodedOrderMapping = null;
+		for (i = 0; i < coeffs.length; i++) {
+			coeffs[i] = null;
+		}
+		coeffs = null;
+		mAlphabet = null;
+		dqc = null;
+		biToMbiMapping = null;
+		lastDc = null;
+		spipp = null;
+		pli = null;
+		coeffsValues = null;
+		planeWidthClip[0] = null;
+		planeWidthClip[1] = null; // No planeWidthClip[2] = null; because third planeWidthClip array is a reused second one.
+		planeWidthClip = null;
+		planeHeightClip[0] = null;
+		planeHeightClip[1] = null; // No planeHeightClip[2] = null; because third planeHeightClip array is a reused second one.
+		planeHeightClip = null;
+		blockCoordinateX = null;
+		blockCoordinateY = null;
+		sbpCodedBits = null;
+		sbfCodedBits = null;
+		bits = null;
+		zeroBytes = null;
+		oneBytes = null;
+		isNb = null;
+		xBLength = null;
+	 	yBLength = null;
+		yBStart = null;
+		yBEnd = null;
+		qcbIndex = root;
+		while (qcbIndex.unmorphableNext != qcbIndex) {
+			Node pointer = qcbIndex.unmorphableNext;
+			
+			qcbIndex = null;
+			qcbIndex = pointer;
+		}
+		qcbIndex = null;
+	}
+
+	private Node rebuildIntraCbIndexQueue(int size, int indices[], Node node) {
+		for (int i = 0; i < size; i++) {
+			node = put(indices[i], node);
+		}
+		return node;
+	}
+	
+	private Node put(int i, Node node) {
+		Node pointer = node;
+		
+		while (pointer.index != i) {
+			pointer = pointer.unmorphableNext;
+		}
+		pointer.previous = node;
+		pointer.next = pointer;
+		
+		node.next = pointer;
+		node = pointer;
+		
+		return node;
+	}
+	
+	private Node generate(int i, Node node) {
+		Node pointer = new Node();
+		
+		node.unmorphableNext = pointer;
+		node = pointer;
+		node.index = i;
+		
+		return node;
+	}
+		
+	private Node remove(Node node) {
+		Node pointer = node.previous;
+		
+		pointer.next = node.next;
+		node = pointer;
+
+		return pointer;
+	}
+	
+	private boolean traversable(Node node) {
+		return node.next != node ? true : false;
+	}
+	
+	private class Node {
+		Node next = this;
+		Node previous;
+		Node unmorphableNext = this;
+		int index = -1;
+	}
+}

Added: trunk/cronus/com/meviatronic/zeus/pollux/VideoReader.java
===================================================================
--- trunk/cronus/com/meviatronic/zeus/pollux/VideoReader.java	                        (rev 0)
+++ trunk/cronus/com/meviatronic/zeus/pollux/VideoReader.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,700 @@
+/* Pollux, a fast Theora decoder created by Michael Scheerer.
+ *
+ * Pollux decoder (c) 2010 Michael Scheerer www.meviatronic.com
+ *
+ * Many thanks to
+ *   Monty <monty at xiph.org> and
+ *   The XIPHOPHORUS Company http://www.xiph.org/ .
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+   
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+package com.meviatronic.zeus.pollux;
+
+import com.meviatronic.zeus.castor.*;
+import com.meviatronic.zeus.helen.*;
+import org.xiph.ogg.*;
+
+import java.io.*;
+import java.awt.*;
+import java.awt.image.*;
+
+/**
+ * The <code>VideoReader</code> class provides all necessary video format detection related methods.
+ * The <code>VideoReader</code> class stors also video header related data.
+ *
+ * @author	Michael Scheerer
+ */
+public final class VideoReader extends AudioReader {
+	
+	private final static byte ZIG_ZAG_SCAN[] = {
+		 0,  1,  5,  6, 14, 15, 27, 28,
+		 2,  4,  7, 13, 16, 26, 29, 42,
+		 3,  8, 12, 17, 25, 30, 41, 43,
+		 9, 11, 18, 24, 31, 40, 44, 53,
+		10, 19, 23, 32, 39, 45, 52, 54,
+		20, 22, 33, 38, 46, 51, 55, 60,
+		21, 34, 37, 47, 50, 56, 59, 61,
+		35, 36, 48, 49, 57, 58, 62, 63
+	};
+	
+	private static TheoraDecoder dec;
+	private static Tag tag;
+	
+	private static int byteIdx;
+	private static byte[] data;
+	private static int bitIdx;
+	private static int packetByteIdx;
+	private static int packetSize;
+	
+	// Theora header
+	static int mbWidth;
+	static int mbHeight;
+	static int sbChromaWidth;
+	static int sbChromaHeight;
+	static int sbLumaWidth;
+	static int sbLumaHeight;
+	public static int codedPictureWidth;
+	public static int codedPictureHeight;
+	public static int pictureRegionW;
+	public static int pictureRegionH;
+	public static int pictureRegionX;
+	public static int pictureRegionY;
+	private static int colorSpace;
+	private static int videoBitrate;
+	private static int qualityHint;
+	static int keyFrameNumberGranuleShift;
+	static int chromaFormat;
+	public static Dimension aspectRatio;
+	public static double frameRate;
+	static int macroblocks;
+	static int superblocks;
+	static int blocks;
+	private static int versionRevisionNumber;
+	
+	private static short[] acScale = new short[64]; // A 64-element array of scale
+	private static short[] dcScale = new short[64];
+	static short [][][][] qmat = new short[2][3][64][64]; // A 64-element array of quantization values for each DCT coefficient in natural order.
+	
+	private static short[][] bms; // A NBMS/64 array containing the base matrices.
+	private static short[][] nqrs = new short[2][3]; // A 2/3 array containing the number of quant
+	// ranges for a given qti and pli, respectively. This is at most 63.
+	private static short[][][] qrSizes = new short[2][3][63]; // A 2/3/63 array of the sizes of
+	// each quant range for a given qti and pli, respectively.
+	// Only the first NQRS[qti ][pli] values are used.
+	private static short[][][] qrbmis = new short[2][3][64]; // A 2/3/64 array of the
+	// bmi’s used for each quant range for a given qti and pli, respectively.
+	// Only the first (NQRS[qti ][pli] + 1) values used
+	private static HuffTreeEntry[] hts = new HuffTreeEntry[80]; // An 80-element array of Huffman tables
+	// with up to 32 entries each.
+	
+	static byte[] lflims = new byte[64];
+	
+	private static boolean headerInitialized1;
+	private static boolean headerInitialized2;
+	private static boolean headerInitialized3;
+	
+	private static int entries;
+	
+	public void loadPacket(byte[] buf, int start, int bytes){
+		byteIdx = start;
+		data = buf;
+		bitIdx = BYTELENGTH;
+		packetByteIdx = 0;
+		packetSize = bytes;
+	}
+
+	private void verifyFirstPacket() throws IOException, EndOfPacketException {
+		// Identification header type 0x80, comment header type 0x81, setup header type 0x82.
+		// This distinguishes them from video data packets
+		// in which the first bit is unset.
+		// packetType = get(8);
+		
+		if (get(8) != 3) {
+			throw new InterruptedIOException("Wrong major version");
+		}
+		if (get(8) != 2) {
+			throw new InterruptedIOException("Wrong minor version");
+		}
+		
+		versionRevisionNumber = get(8);
+		
+		codedPictureWidth = (mbWidth = get(16)) << 4;
+		codedPictureHeight = (mbHeight = get(16)) << 4;
+		pictureRegionW = get(24);
+		pictureRegionH = get(24);
+		pictureRegionX = get(8);
+		// Theora uses a right-handed coordinate system, while applications expect a left-handed one, so:
+		pictureRegionY = codedPictureHeight - pictureRegionH - get(8);
+
+		int framerateNumerator = get(32);
+		
+		int framerateDenominator = get(32);
+		
+		int aspectRatioNumerator = get(24);
+		
+		int aspectRatioDenominator = get(24);
+		
+		colorSpace = get(8);
+		videoBitrate = get(24);
+		qualityHint = get(6);
+		keyFrameNumberGranuleShift = get(5);
+		chromaFormat = get(2);
+		if (get(3) != 0) {
+			throw new InterruptedIOException("Reserved values not zero");
+		}
+
+		if (codedPictureWidth * codedPictureHeight > Integer.MAX_VALUE) {
+			throw new InterruptedIOException("Resolution overflow - Java cannot handle images with the size 2^40");
+		}
+		if (codedPictureWidth < pictureRegionW || codedPictureHeight < pictureRegionH) {
+			throw new InterruptedIOException("Picture region must be less/equal the coded frame");
+		}
+		if (mbWidth == 0 || mbHeight == 0) {
+			throw new InterruptedIOException("Wrong frame size in macroblocks");
+		}
+		if (pictureRegionW + pictureRegionX > codedPictureWidth || pictureRegionH + pictureRegionY > codedPictureHeight) {
+			throw new InterruptedIOException("Wrong picture region offset in pixels");
+		}
+		if (framerateNumerator == 0 || framerateDenominator == 0) {
+			throw new InterruptedIOException("Wrong frame rate");
+		} else {
+			frameRate = framerateNumerator / (double) framerateDenominator;
+		}
+		if (aspectRatioNumerator == 0 || aspectRatioDenominator == 0) {
+			aspectRatioNumerator = 1;
+			aspectRatioDenominator = 1;
+		}
+				
+		aspectRatio = new Dimension(aspectRatioNumerator, aspectRatioDenominator);
+			
+		if (chromaFormat == 1) {
+			throw new InterruptedIOException("Wrong color space");
+		}
+
+		macroblocks = mbWidth * mbHeight;
+		sbLumaWidth = (mbWidth + 1) / 2;
+		sbLumaHeight = (mbHeight + 1) / 2;
+		if (chromaFormat == 0) {
+			sbChromaWidth = (mbWidth + 3) / 4;
+			sbChromaHeight = (mbHeight + 3) / 4;
+			blocks = macroblocks * 6;
+		} else if (chromaFormat == 2) {
+			sbChromaWidth = (mbWidth + 3) / 4;
+			sbChromaHeight = (mbHeight + 1) / 2;
+			blocks = macroblocks * 8;
+		} else {
+			sbChromaWidth = (mbWidth + 1) / 2;
+			sbChromaHeight = (mbHeight + 1) / 2;
+			blocks = macroblocks * 12;
+		}
+		superblocks = sbLumaWidth * sbLumaHeight + 2 * sbChromaWidth * sbChromaHeight;
+		headerInitialized1 = true;
+	}
+	
+	private final void verifySecondPacket() throws IOException, EndOfPacketException {
+		try {
+			tag = new OggTag(this, false);
+			((OggTag) tag).decode();
+		} catch (Exception e) {
+			if (e instanceof IOException) {
+				throw new InterruptedIOException(e.getMessage());
+			}
+		}
+		headerInitialized2 = true;
+	}
+
+	private final void verifyThirdPacket() throws IOException, EndOfPacketException {
+		
+		// 6.4.1 Loop Filter Limit Table Decode
+		
+		int nbits = get(3); // The size of values being read in the current table.
+		
+		int qi; // The quantization index.
+		
+		for (qi = 0; qi < 64; qi++) {
+			lflims[qi] = (byte) get(nbits);
+		}
+		
+		// 6.4.2 Quantization Parameters Decode
+		
+		int qti; // A quantization type index. See Table 3.1.
+		int qtj; // A quantization type index.
+		int pli; // A color plane index. See Table 2.1.
+		int plj; // A color plane index.
+		int ci;  // The DCT coefficient index.
+		int bmi; // The base matrix index.
+		int qri; // The quant range index.
+		int newqr; // Flag that indicates a new set of quant ranges will be defined.
+		int rpqr; // Flag that indicates the quant ranges o copy will come from the same color plane.
+		int nbms; // The number of base matrices
+	
+		nbits = get(4) + 1;
+		
+		for (qi = 0; qi < 64; qi++) {
+			acScale[qi] = (short) get(nbits);
+		}
+		
+		nbits = get(4) + 1;
+		
+		for (qi = 0; qi < 64; qi++) {
+			dcScale[qi] = (short) get(nbits);
+		}
+		
+		nbms = get(9) + 1;
+		
+		if (nbms > 384) {
+			throw new InterruptedIOException("Base matrice number to high");
+		}
+		
+		bms = new short[nbms][64];
+		short[] bmsPointer;
+		
+		for (bmi = 0; bmi < nbms; bmi++) {
+			bmsPointer = bms[bmi];
+			for (ci = 0; ci < 64; ci++) {
+				bmsPointer[ci] = (short) get(8);
+			}
+		}
+		
+		for (qti = 0; qti < 2; qti++) {
+			for (pli = 0; pli < 3; pli++) {
+				if (qti > 0 || pli > 0) {
+					newqr = get1();
+				} else {
+					newqr = 1;
+				}
+				if (newqr == 0) {
+					if (qti > 0) {
+						rpqr = get1();
+					} else {
+						rpqr = 0;
+					}
+					if (rpqr == 1) {
+						qtj = qti - 1;
+						plj = pli;
+					} else {
+						qtj = (3 * qti + pli - 1) / 3;
+						plj = (pli + 2) % 3;
+					}
+					nqrs[qti][pli] = nqrs[qtj][plj ];
+					qrSizes[qti][pli] = qrSizes[qtj][plj];
+					qrbmis[qti][pli] = qrbmis[qtj][plj];
+				} else {
+					qri = qi = 0;
+
+					int check = get(ilog(nbms - 1));
+					
+					if (check >= nbms) {
+						throw new InterruptedIOException("Quant range size exceeds base matrice number");
+					}
+					qrbmis[qti][pli][qri] = (short) check;
+					
+					do {
+						qrSizes[qti][pli][qri] = (short) (get(ilog(62 - qi)) + 1);
+						qi += qrSizes[qti][pli][qri];
+						qri++;
+						qrbmis[qti][pli][qri] = (short) get(ilog(nbms - 1));
+					} while (qi < 63);
+					if (qi > 63) {
+						throw new InterruptedIOException("Quantization index exceeds 63");
+					}
+					nqrs[qti][pli] = (short) qri;
+				}
+			}
+		}
+
+		// 6.4.4 DCT Token Huffman Tables
+		
+		int hti; // The index of the current Huffman table to use.
+		
+		HuffTreeEntry entry;
+		
+		for (hti = 0; hti < 80; hti++) {
+			entry = new HuffTreeEntry();
+			entries = 0;
+			buildTree(entry, 0);
+			if (!entry.sparse) {
+				deflateTree(entry);
+				pruneTree(entry);
+			}
+			hts[hti] = entry;
+		}
+		
+		// 6.4.3 Computing all 384 Quantization Matrixes
+		
+		short[][][] qmatOuterPointer;
+		short[][] qmatInnerPointer;
+		
+		for (qti = 0; qti < qmat.length; qti++) {
+			qmatOuterPointer = qmat[qti];
+			for (pli = 0; pli < qmatOuterPointer.length; pli++) {
+				qmatInnerPointer = qmatOuterPointer[pli];
+				for (qi = 0; qi < qmatInnerPointer.length; qi++) {
+					computeQuantizationMatrix(qmatInnerPointer[qi], qti, pli, qi);
+				}
+			}
+		}
+		headerInitialized3 = true;
+	}
+	// 6.4.3 Computing a Quantization Matrix
+	private short[] computeQuantizationMatrix(short[] qmat, int qti, int pli, int qi) {
+		int ci; // The DCT coefficient index.
+		int bmi; // The base matrix index.
+		int bmj; // The base matrix index.
+		int qri; // The quant range index.
+		int qiStart = 0; // The left end-point of the qi range.
+		int qiEnd = 0; // The right end-point of the qi range.
+		int qmin; // The minimum quantization value allowed for the current coefficient.
+		int qscale; // The current scale value.
+		
+		for (qri = 0; qri < 63; qri++) {
+			qiEnd += qrSizes[qti][pli][qri];
+			if (qiEnd >= qi && qiStart <= qi) {
+				break;
+			}
+			qiStart = qiEnd;
+		}
+		
+		short qrSizesValue = qrSizes[qti][pli][qri];
+		int bmci; // A value containing the interpolated base matrix.
+		
+		bmi = qrbmis[qti][pli][qri];
+		bmj = qrbmis[qti][pli][qri + 1];
+		
+		short[] bmsPointer = bms[bmi];
+		short[] bmsPointer1 = bms[bmj];
+		
+		for (ci = 0, qmin = 16; ci < 64; ci++) {
+			bmci = (2 * (qiEnd - qi) * bmsPointer[ci] + 2 * (qi - qiStart) * bmsPointer1[ci] + qrSizesValue) / (2 * qrSizesValue);
+			if (ci > 0 && qti == 0) {
+				qmin = 8;
+			} else if (ci == 0 && qti == 1) {
+				qmin = 32;
+			}
+			if (ci ==0) {
+				qscale = dcScale[qi];
+			} else {
+				qscale = acScale[qi];
+			}
+			qmat[ZIG_ZAG_SCAN[ci]] = (short) Math.max(qmin, Math.min((qscale * bmci / 100) << 2, 4096));
+		}
+			
+		return qmat;
+	}
+	
+	public void readMediaInformation(Packet op) throws IOException, EndOfPacketException {
+		
+		if(op == null) {
+			throw new InterruptedIOException("Packet is null");
+		}
+
+		loadPacket(op.packetBase, op.packet, op.bytes);
+	
+        byte[] buffer = new byte[6];
+		
+		int packetType = get(8);
+		
+		for (int i = 0; i < buffer.length; i++) {
+			buffer[i] = (byte) get(8);
+		}
+		
+		if (!new String(buffer).equals("theora")) {
+			throw new InterruptedIOException("No first packet");
+		}
+	    
+		if (packetType == 0x80 && op.bos != 0) {
+			if(headerInitialized1) {
+          		return;
+        	}
+			verifyFirstPacket();
+		} else if (packetType == 0x81 && op.bos == 0) {
+			if(headerInitialized2) {
+          		return;
+        	}
+			verifySecondPacket();
+		} else if (packetType == 0x82 && op.bos == 0) {
+			if(headerInitialized3) {
+          		return;
+        	}
+			verifyThirdPacket();
+		} else {
+			throw new InterruptedIOException("Wrong packet order");
+		}
+	}
+
+	public TheoraDecoder initializeTheoraDecoder() {
+		if (dec == null) {
+			dec = new TheoraDecoder(this);
+		}
+		dec.granulepos = -1;
+		return dec;
+	}
+	
+	public void forceReinitialization() {
+		if (dec != null) {
+			dec.close();
+			close();
+			dec = null;
+		}
+		headerInitialized1 = headerInitialized2 = headerInitialized3 = false;
+	}
+	
+	int get1() throws IOException, EndOfPacketException {
+		if (packetSize == 0) {
+			throw new EndOfPacketException();
+		}
+		if (packetByteIdx >= packetSize && packetSize > 0) {
+			throw new InterruptedIOException("Illegal truncate packet decoding mode");
+		}
+
+		bitIdx--;
+		
+		int val = data[byteIdx] >>> bitIdx & 1;
+
+		if (bitIdx == 0) {
+			bitIdx = BYTELENGTH;
+			byteIdx++;
+			packetByteIdx++;
+		}
+		return val;
+	}
+	
+	/**
+	 * Returns an integer with the length i
+	 *
+	 * @return                               the integer value
+	 * @param i                              the length in bits
+	 * @exception IOException                if the bits can't be retrieved
+	 * @exception EndOfPacketExeption        if an end of packet occur
+	 */
+	public int get(int i) throws IOException, EndOfPacketException {
+		if (i <= 0) {
+			return 0;
+		}
+		if (packetSize == 0) {
+			throw new EndOfPacketException();
+		}
+		if (packetByteIdx >= packetSize && packetSize > 0) {
+			throw new InterruptedIOException("Illegal truncate packet decoding mode");
+		}
+
+		int val = 0;
+		
+		int prefix = data[byteIdx] & BITMASK[bitIdx];
+		
+		bitIdx -= i;
+		
+		if (bitIdx > 0) {
+			return prefix >>> bitIdx;
+		} else {
+			bitIdx += BYTELENGTH;
+			byteIdx++;
+			packetByteIdx++;
+			if (bitIdx < BYTELENGTH) {
+				if (packetByteIdx >= packetSize) {
+					throw new InterruptedIOException("Illegal truncate packet decoding mode");
+				}
+				val = data[byteIdx] & 0xFF;
+			}
+			if (bitIdx <= 0) {
+				bitIdx += BYTELENGTH;
+				val <<= BYTELENGTH;
+				prefix <<= BYTELENGTH;
+				byteIdx++;
+				packetByteIdx++;
+				if (bitIdx < BYTELENGTH) {
+					if (packetByteIdx >= packetSize) {
+						throw new InterruptedIOException("Illegal truncate packet decoding mode");
+					}
+					val |= data[byteIdx] & 0xFF;
+				}
+				if (bitIdx <= 0) {
+					bitIdx += BYTELENGTH;
+					val <<= BYTELENGTH;
+					prefix <<= BYTELENGTH;
+					byteIdx++;
+					packetByteIdx++;
+					if (bitIdx < BYTELENGTH) {
+						if (packetByteIdx >= packetSize) {
+							throw new InterruptedIOException("Illegal truncate packet decoding mode");
+						}
+						val |= data[byteIdx] & 0xFF;
+					}
+					if (bitIdx <= 0) {
+						bitIdx += BYTELENGTH;
+						val <<= BYTELENGTH;
+						prefix <<= BYTELENGTH;
+						byteIdx++;
+						packetByteIdx++;
+						if (bitIdx < BYTELENGTH) {
+							if (packetByteIdx >= packetSize) {
+								throw new InterruptedIOException("Illegal truncate packet decoding mode");
+							}
+							val |= data[byteIdx] & 0xFF;
+						}
+					}
+				}
+			}
+		}
+		val >>>= bitIdx;
+		val |= prefix << BYTELENGTH - bitIdx;
+		return val;
+	}
+
+	public String getOggCommentContent() {
+		return tag.toString();
+	}
+
+	final int getCodeWord(int bookNumber) throws IOException, EndOfPacketException {
+		HuffTreeEntry node = hts[bookNumber];
+		
+		if (node.sparse) {
+			return node.value;
+		}
+		while (node.value == -1){
+			node = node.childFeed[get(node.feed)];
+			if (node == null) {
+				return -1;
+			}
+		}
+		return node.value;
+	}
+	
+	/**
+	 * Frees all system resources, which are bounded to this object.
+	 */
+	public void close() {
+		super.close();
+		int i, j, k;
+		
+		if (hts[0] != null) {
+			for (i = 0; i < 80; i++) {
+				closeTree(hts[i]);
+				hts[i] = null;
+			}
+		} else {
+			return;
+		}
+		data = null;
+		for (i = 0; i < bms.length; i++) {
+			bms[i] = null;
+		}
+		bms = null;
+	}
+
+	private void deflateTree(HuffTreeEntry node) {
+		int i, j, k, feedMinusOne, feedMinusOneMinusJ;
+		HuffTreeEntry nodeBase = node;
+			
+		for (node.feed = 2; node.feed < 33; node.feed++) {
+			k = 1 << node.feed;
+			HuffTreeEntry[] copy = new HuffTreeEntry[k];
+			
+			feedMinusOne = node.feed - 1;
+			for (i = 0; i < k; i++) {
+				for (j = 0; j < node.feed; j++) {
+					feedMinusOneMinusJ = feedMinusOne - j;
+					nodeBase = nodeBase.child[(i & 1 << feedMinusOneMinusJ) >>> feedMinusOneMinusJ];
+					if (nodeBase == null) {
+						node.feed--;
+						copy = null;
+						if (node.feed > 1) {
+							for (i = 0; i < node.childFeed.length; i++) {
+								deflateTree(node.childFeed[i]);
+							}
+						}
+						return;
+					}
+				}
+				copy[i] = nodeBase;
+				nodeBase = node;
+			}
+			for (i = 0; i < node.childFeed.length; i++) {
+				node.childFeed[i].dereferenced = true;
+			}
+			node.childFeed = copy;
+		}
+	}
+	
+	private void buildTree(HuffTreeEntry node, int depth) throws IOException, EndOfPacketException {
+		if(get1() == 1) {
+			if (depth == 0) {
+				node.sparse = true;
+			}
+			if (++entries > 32) {
+				throw new InterruptedIOException("Huffmann table entry overflow");
+			}
+			node.value = get(5);
+			return;
+		}
+		if (++depth > 32) {
+			throw new InterruptedIOException("Huffmann table depht overflow");
+		}
+		buildTree((node.child[0] = new HuffTreeEntry()), depth);
+		buildTree((node.child[1] = new HuffTreeEntry()), depth);
+	}
+	
+	private void pruneTree(HuffTreeEntry node) {
+		HuffTreeEntry left = node.child[0];
+		HuffTreeEntry right = node.child[1];
+		
+		if (left != null) {
+			pruneTree(left);
+			pruneTree(right);
+		}
+		if (node.dereferenced) {
+			node.child = null;
+			node = null;
+		} else {
+			if (node.child != node.childFeed) {
+				node.child = null;
+			}
+		}
+	}
+	
+	private void closeTree(HuffTreeEntry node) {
+		if (node.sparse) {
+			node.child = null;
+			node = null;
+			return;
+		}
+
+		HuffTreeEntry nodeBase;
+		
+		for (int i = 0; i < node.childFeed.length; i++) {
+			nodeBase = node.childFeed[i];
+			if (nodeBase != null) {
+				closeTree(nodeBase);
+				nodeBase = null;
+			}
+		}
+		node.child = null;
+		node.childFeed = null;
+		node = null;
+	}
+	
+	private class HuffTreeEntry {
+		HuffTreeEntry[] child = new HuffTreeEntry[2];
+		HuffTreeEntry[] childFeed = child;
+		int value = -1;
+		boolean sparse;
+		byte feed = 1;
+		boolean dereferenced;
+	}
+}
+
+

Added: trunk/cronus/org/xiph/ogg/Packet.java
===================================================================
--- trunk/cronus/org/xiph/ogg/Packet.java	                        (rev 0)
+++ trunk/cronus/org/xiph/ogg/Packet.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,51 @@
+/* Ogg, created by Xiph,
+ * ported from C++ to Java and named as Jorbis framework.
+ *
+ * Ogg decoder (c) 2000 Xiph foundation www.xiph.org
+ * Jorbis framework (c) 2000 ymnk, JCraft,Inc. <ymnk at jcraft.com>
+ *
+ * Many thanks to
+ *   Monty <monty at xiph.org> and
+ *   The XIPHOPHORUS Company http://www.xiph.org/ .
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+   
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+package org.xiph.ogg;
+
+/**
+ * The <code>Packet</code> class provides all necessary packet related members.
+ *
+ * @author	Xip, JCraft (Port)
+ */
+public final class Packet{
+	public byte[] packetBase;
+	public int packet;
+	public int bytes;
+	public int bos;
+	public int eos;
+
+	public long granulepos;
+
+    /**
+     * sequence number for decode; the framing
+     * knows where there's a hole in the data,
+     * but we need coupling so that the codec
+     * (which is in a seperate abstraction
+     * layer) also knows about the gap
+     */
+	public long packetno;
+}

Added: trunk/cronus/org/xiph/ogg/Page.java
===================================================================
--- trunk/cronus/org/xiph/ogg/Page.java	                        (rev 0)
+++ trunk/cronus/org/xiph/ogg/Page.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,142 @@
+/* Ogg, created by Xiph,
+ * ported from C++ to Java and named as Jorbis framework.
+ *
+ * Ogg decoder (c) 2000 Xiph foundation www.xiph.org
+ * Jorbis framework (c) 2000 ymnk, JCraft,Inc. <ymnk at jcraft.com>
+ *
+ * Many thanks to
+ *   Monty <monty at xiph.org> and
+ *   The XIPHOPHORUS Company http://www.xiph.org/ .
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+   
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package org.xiph.ogg;
+
+/**
+ * The <code>Page</code> class provides all necessary page related members.
+ * The CRC algorithm is delevoped based on public domain code by
+ * Ross Williams (ross at guest.adelaide.edu.au).
+ * A 32 bit CRC value (direct algorithm, initial val and final XOR = 0, generator polynomial=0x04c11db7) is used.
+ * The value is computed over the entire header (with the CRC field in the header set to zero)
+ * and then continued over the page.
+ * The CRC field is then filled with the computed value.
+ * This algorithm is taken from the Castor decoder.
+ *
+ * @author Xiph, JCraft (Port), Michael Scheerer (CRC)
+ */
+
+
+public final class Page {
+	
+	private static int[] crcTable;
+	
+	static {
+		int i, j, r;
+		
+		crcTable = new int[256];
+		
+		for (i = 0; i < 256; i++) {
+			r = i << 24;
+			
+			for (j = 0; j < 8; j++) {
+				if ((r & 0x80000000) != 0) {
+					r = r << 1 ^ 0x04C11DB7;
+				} else {
+					r <<= 1;
+				}
+			}
+			crcTable[i] = r;
+		}
+	}
+
+
+  	public byte[] header_base;
+  	public int header;
+  	public int header_len;
+  	public byte[] body_base;
+  	public int body;
+  	public int body_len;
+
+  	int version(){
+    	return header_base[header + 4] & 0xFF;
+  	}
+
+  	int continued() {
+    	return (header_base[header + 5] & 0x01);
+  	}
+
+  	public int bos() {
+    	return (header_base[header + 5] & 0x02);
+  	}
+
+  	public int eos() {
+    	return (header_base[header + 5] & 0x04);
+  	}
+
+  	public long granulepos() {
+    	long foo=header_base[header + 13] & 0xFF;
+    	foo = (foo << 8) | (header_base[header + 12] & 0xFF);
+    	foo = (foo << 8) | (header_base[header + 11] & 0xFF);
+    	foo = (foo << 8) | (header_base[header + 10] & 0xFF);
+    	foo = (foo << 8) | (header_base[header + 9] & 0xFF);
+    	foo = (foo << 8) | (header_base[header + 8] & 0xFF);
+    	foo = (foo << 8) | (header_base[header + 7] & 0xFF);
+    	foo = (foo << 8) | (header_base[header + 6] & 0xFF);
+    	return (foo);
+  	}
+
+  	public int serialno() {
+    	return (header_base[header + 14] & 0xFF) | ((header_base[header + 15] & 0xFF) << 8)
+        | ((header_base[header + 16] & 0xFF) << 16)
+        | ((header_base[header + 17] & 0xFF) << 24);
+  	}
+
+  	int pageno() {
+    	return (header_base[header + 18] & 0xFF) | ((header_base[header + 19] & 0xFF) << 8)
+        | ((header_base[header + 20] & 0xFF) << 16)
+        | ((header_base[header + 21] & 0xFF) << 24);
+  	}
+	
+	private int getChecksum(int crc, byte[] data, int i, int j) {
+		int k = i + j;
+		
+		for (; i < k; i++) {
+			crc = crc << 8 ^ crcTable[crc >>> 24 & 0xFF ^ data[i] & 0xFF];
+		}
+		return crc;
+	}
+	
+	private static int getChecksum(int crc, byte data) {
+		return crc << 8 ^ crcTable[crc >>> 24 & 0xFF ^ data & 0xFF];
+	}
+	
+	void checksum(){
+		int crc_reg = 0;
+	  
+	 	crc_reg = getChecksum(crc_reg, header_base, header, header_len);
+	 	crc_reg = getChecksum(crc_reg, body_base, body, body_len);
+	 
+    	header_base[header + 22] = (byte)crc_reg;
+    	header_base[header + 23] = (byte)(crc_reg >>> 8);
+    	header_base[header + 24] = (byte)(crc_reg >>> 16);
+    	header_base[header + 25] = (byte)(crc_reg >>> 24);
+	}
+	
+	public void close () {
+		header_base = null;
+		body_base = null;
+	}
+}

Added: trunk/cronus/org/xiph/ogg/StreamState.java
===================================================================
--- trunk/cronus/org/xiph/ogg/StreamState.java	                        (rev 0)
+++ trunk/cronus/org/xiph/ogg/StreamState.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,331 @@
+/* Ogg, created by Xiph,
+ * ported from C++ to Java and named as Jorbis framework.
+ *
+ * Ogg decoder (c) 2000 Xiph foundation www.xiph.org
+ * Jorbis framework (c) 2000 ymnk, JCraft,Inc. <ymnk at jcraft.com>
+ *
+ * Many thanks to
+ *   Monty <monty at xiph.org> and
+ *   The XIPHOPHORUS Company http://www.xiph.org/ .
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+   
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+package org.xiph.ogg;
+
+/**
+ * @author Xiph, JCraft (Port)
+ */
+public final class StreamState{
+  byte[] body_data; /* bytes from packet bodies */
+  int body_storage; /* storage elements allocated */
+  int body_fill; /* elements stored; fill mark */
+  private int body_returned; /* elements of fill returned */
+
+  int[] lacing_vals; /* The values that will go to the segment table */
+  long[] granule_vals; /* pcm_pos values for headers. Not compact
+   		   this way, but it is simple coupled to the
+   		   lacing fifo */
+  int lacing_storage;
+  int lacing_fill;
+  int lacing_packet;
+  int lacing_returned;
+
+  byte[] header=new byte[282]; /* working space for header encode */
+  int header_fill;
+
+  private int eos; /* set when we have buffered the last packet in the
+   	 logical bitstream */
+  private int bos; /* set after we've written the initial page
+   of a logical bitstream */
+  int serialno;
+  int pageno;
+  long packetno; /* sequence number for decode; the framing
+                      knows where there's a hole in the data,
+                      but we need coupling so that the codec
+                      (which is in a seperate abstraction
+                      layer) also knows about the gap */
+  long granulepos;
+
+  public StreamState(){
+    init();
+  }
+
+  StreamState(int serialno){
+    this();
+    init(serialno);
+  }
+
+  void init(){
+    body_storage=16*1024;
+    body_data=new byte[body_storage];
+    lacing_storage=1024;
+    lacing_vals=new int[lacing_storage];
+    granule_vals=new long[lacing_storage];
+  }
+
+  public void init(int serialno){
+    if(body_data==null){
+      init();
+    }
+    else{
+      for(int i=0; i<body_data.length; i++)
+        body_data[i]=0;
+      for(int i=0; i<lacing_vals.length; i++)
+        lacing_vals[i]=0;
+      for(int i=0; i<granule_vals.length; i++)
+        granule_vals[i]=0;
+    }
+    this.serialno=serialno;
+  }
+
+	
+	public void close() {
+    	body_data = null;
+    	lacing_vals = null;
+		granule_vals = null;
+		header = null;
+  	}
+
+  private void body_expand(int needed){
+    if(body_storage<=body_fill+needed){
+      body_storage+=(needed+1024);
+      byte[] foo=new byte[body_storage];
+      System.arraycopy(body_data, 0, foo, 0, body_data.length);
+      body_data=foo;
+    }
+  }
+
+  private void lacing_expand(int needed){
+    if(lacing_storage<=lacing_fill+needed){
+      lacing_storage+=(needed+32);
+      int[] foo=new int[lacing_storage];
+      System.arraycopy(lacing_vals, 0, foo, 0, lacing_vals.length);
+      lacing_vals=foo;
+
+      long[] bar=new long[lacing_storage];
+      System.arraycopy(granule_vals, 0, bar, 0, granule_vals.length);
+      granule_vals=bar;
+    }
+  }
+
+  public int packetout(Packet op){
+
+    /* The last part of decode. We have the stream broken into packet
+       segments.  Now we need to group them into packets (or return the
+       out of sync markers) */
+
+    int ptr=lacing_returned;
+
+    if(lacing_packet<=ptr){
+      return (0);
+    }
+
+    if((lacing_vals[ptr]&0x400)!=0){
+      /* We lost sync here; let the app know */
+      lacing_returned++;
+
+      /* we need to tell the codec there's a gap; it might need to
+         handle previous packet dependencies. */
+      packetno++;
+      return (-1);
+    }
+
+    /* Gather the whole packet. We'll have no holes or a partial packet */
+    {
+      int size=lacing_vals[ptr] & 0xff;
+      int bytes=0;
+
+      op.packetBase = body_data;
+      op.packet = body_returned;
+      op.eos = lacing_vals[ptr] & 0x200; /* last packet of the stream? */
+      op.bos = lacing_vals[ptr] & 0x100; /* first packet of the stream? */
+      bytes+=size;
+
+      while(size == 255){
+        int val = lacing_vals[++ptr];
+        size = val & 0xff;
+        if((val & 0x200) !=0 )
+          op.eos = 0x200;
+        bytes += size;
+      }
+
+      op.packetno=packetno;
+		op.granulepos=granule_vals[ptr];
+		
+      op.bytes=bytes;
+
+      body_returned+=bytes;
+
+      lacing_returned=ptr+1;
+    }
+    packetno++;
+    return (1);
+  }
+
+  // add the incoming page to the stream state; we decompose the page
+  // into packet segments here as well.
+
+  public int pagein(Page og){
+    byte[] header_base=og.header_base;
+    int header=og.header;
+    byte[] body_base=og.body_base;
+    int body=og.body;
+    int bodysize=og.body_len;
+    int segptr=0;
+
+    int version=og.version();
+    int continued=og.continued();
+    int bos=og.bos();
+    int eos=og.eos();
+    long granulepos=og.granulepos();
+    int _serialno=og.serialno();
+    int _pageno=og.pageno();
+    int segments=header_base[header+26]&0xff;
+
+    // clean up 'returned data'
+    {
+      int lr=lacing_returned;
+      int br=body_returned;
+
+      // body data
+      if(br!=0){
+        body_fill-=br;
+        if(body_fill!=0){
+          System.arraycopy(body_data, br, body_data, 0, body_fill);
+        }
+        body_returned=0;
+      }
+
+      if(lr!=0){
+        // segment table
+        if((lacing_fill-lr)!=0){
+          System.arraycopy(lacing_vals, lr, lacing_vals, 0, lacing_fill-lr);
+          System.arraycopy(granule_vals, lr, granule_vals, 0, lacing_fill-lr);
+        }
+        lacing_fill-=lr;
+        lacing_packet-=lr;
+        lacing_returned=0;
+      }
+    }
+
+    // check the serial number
+    if(_serialno!=serialno)
+      return (-1);
+    if(version>0)
+      return (-1);
+
+    lacing_expand(segments+1);
+
+    // are we in sequence?
+    if(_pageno!=pageno){
+      int i;
+
+      // unroll previous partial packet (if any)
+      for(i=lacing_packet; i<lacing_fill; i++){
+        body_fill-=lacing_vals[i]&0xff;
+        //System.out.println("??");
+      }
+      lacing_fill=lacing_packet;
+
+      // make a note of dropped data in segment table
+      if(pageno!=-1){
+        lacing_vals[lacing_fill++]=0x400;
+        lacing_packet++;
+      }
+
+      // are we a 'continued packet' page?  If so, we'll need to skip
+      // some segments
+      if(continued!=0){
+        bos=0;
+        for(; segptr<segments; segptr++){
+          int val=(header_base[header+27+segptr]&0xff);
+          body+=val;
+          bodysize-=val;
+          if(val<255){
+            segptr++;
+            break;
+          }
+        }
+      }
+    }
+
+    if(bodysize!=0){
+      body_expand(bodysize);
+      System.arraycopy(body_base, body, body_data, body_fill, bodysize);
+      body_fill+=bodysize;
+    }
+
+    {
+      int saved=-1;
+      while(segptr<segments){
+        int val=(header_base[header+27+segptr]&0xff);
+        lacing_vals[lacing_fill]=val;
+        granule_vals[lacing_fill]=-1;
+
+        if(bos!=0){
+          lacing_vals[lacing_fill]|=0x100;
+          bos=0;
+        }
+
+        if(val<255)
+          saved=lacing_fill;
+
+        lacing_fill++;
+        segptr++;
+
+        if(val<255)
+          lacing_packet=lacing_fill;
+      }
+
+      /* set the granulepos on the last pcmval of the last full packet */
+      if(saved!=-1){
+        granule_vals[saved]=granulepos;
+      }
+    }
+
+    if(eos!=0){
+      eos=1;
+      if(lacing_fill>0)
+        lacing_vals[lacing_fill-1]|=0x200;
+    }
+
+    pageno=_pageno+1;
+    return (0);
+  }
+
+  public int eof(){
+    return eos;
+  }
+
+  public int reset(){
+    body_fill=0;
+    body_returned=0;
+
+    lacing_fill=0;
+    lacing_packet=0;
+    lacing_returned=0;
+
+    header_fill=0;
+
+    eos=0;
+    bos=0;
+    pageno=-1;
+    packetno=0;
+    granulepos=0;
+    return (0);
+  }
+}

Added: trunk/cronus/org/xiph/ogg/SyncState.java
===================================================================
--- trunk/cronus/org/xiph/ogg/SyncState.java	                        (rev 0)
+++ trunk/cronus/org/xiph/ogg/SyncState.java	2010-03-27 16:19:37 UTC (rev 17093)
@@ -0,0 +1,282 @@
+/* Ogg, created by Xiph,
+ * ported from C++ to Java and named as Jorbis framework.
+ *
+ * Ogg decoder (c) 2000 Xiph foundation www.xiph.org
+ * Jorbis framework (c) 2000 ymnk, JCraft,Inc. <ymnk at jcraft.com>
+ *
+ * Many thanks to
+ *   Monty <monty at xiph.org> and
+ *   The XIPHOPHORUS Company http://www.xiph.org/ .
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+   
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+package org.xiph.ogg;
+
+// DECODING PRIMITIVES: packet streaming layer
+
+// This has two layers to place more of the multi-serialno and paging
+// control in the application's hands.  First, we expose a data buffer
+// using ogg_decode_buffer().  The app either copies into the
+// buffer, or passes it directly to read(), etc.  We then call
+// ogg_decode_wrote() to tell how many bytes we just added.
+//
+// Pages are returned (pointers into the buffer in ogg_sync_state)
+// by ogg_decode_stream().  The page is then submitted to
+// ogg_decode_page() along with the appropriate
+// ogg_stream_state* (ie, matching serialno).  We then get raw
+// packets out calling ogg_stream_packet() with a
+// ogg_stream_state.  See the 'frame-prog.txt' docs for details and
+// example code.
+
+// Race condition killed by MS (www.meviatronic.de).
+
+/**
+ * @author Xiph, JCraft (Port)
+ */
+public final class SyncState{
+	
+	// sync the stream.  This is meant to be useful for finding page
+  // boundaries.
+  //
+  // return values for this:
+  // -n) skipped n bytes
+  //  0) page not ready; more data (no bytes skipped)
+  //  n) page synced at current location; page length n bytes
+  private Page pageseek=new Page();
+  private byte[] chksum=new byte[4];
+	
+  public byte[] data;
+  int storage;
+  int fill;
+  int returned;
+
+  int unsynced;
+  int headerbytes;
+  int bodybytes;
+
+  public void close(){
+	  data=null;
+	  pageseek.close();
+	  pageseek = null;
+	  chksum = null;
+  }
+
+	public int buffer(int size){
+    // first, clear out any space that has been previously returned
+    if(returned!=0){
+      fill-=returned;
+      if(fill>0){
+        System.arraycopy(data, returned, data, 0, fill);
+      }
+      returned=0;
+    }
+
+    if(size>storage-fill){
+      // We need to extend the internal buffer
+      int newsize=size+fill+4096; // an extra page to be nice
+      if(data!=null){
+        byte[] foo=new byte[newsize];
+        System.arraycopy(data, 0, foo, 0, data.length);
+        data=foo;
+      }
+      else{
+        data=new byte[newsize];
+      }
+      storage=newsize;
+    }
+
+    return (fill);
+  }
+
+  public int wrote(int bytes){
+    if(fill+bytes>storage)
+      return (-1);
+    fill+=bytes;
+    return (0);
+  }
+
+  private int pageseek(Page og){
+    int page=returned;
+    int next;
+    int bytes=fill-returned;
+	  
+    if(headerbytes==0){
+      int _headerbytes, i;
+      if(bytes<27)
+        return (0); // not enough for a header
+
+      /* verify capture pattern */
+      if(data[page]!='O'||data[page+1]!='g'||data[page+2]!='g'
+          ||data[page+3]!='S'){
+        headerbytes=0;
+        bodybytes=0;
+
+        // search for possible capture
+        next=0;
+        for(int ii=0; ii<bytes-1; ii++){
+          if(data[page+1+ii]=='O'){
+            next=page+1+ii;
+            break;
+          }
+        }
+        //next=memchr(page+1,'O',bytes-1);
+        if(next==0)
+          next=fill;
+
+        returned=next;
+        return (-(next-page));
+      }
+      _headerbytes=(data[page+26]&0xff)+27;
+      if(bytes<_headerbytes)
+        return (0); // not enough for header + seg table
+
+      // count up body length in the segment table
+
+      for(i=0; i<(data[page+26]&0xff); i++){
+        bodybytes+=(data[page+27+i]&0xff);
+      }
+      headerbytes=_headerbytes;
+    }
+
+    if(bodybytes+headerbytes>bytes)
+      return (0);
+
+    // The whole test page is buffered.  Verify the checksum
+    //synchronized(chksum){
+      // Grab the checksum bytes, set the header field to zero
+
+      System.arraycopy(data, page+22, chksum, 0, 4);
+      data[page+22]=0;
+      data[page+23]=0;
+      data[page+24]=0;
+      data[page+25]=0;
+
+      // set up a temp page struct and recompute the checksum
+      Page log=pageseek;
+      log.header_base=data;
+      log.header=page;
+      log.header_len=headerbytes;
+
+      log.body_base=data;
+      log.body=page+headerbytes;
+      log.body_len=bodybytes;
+      log.checksum();
+
+      // Compare
+      if(chksum[0]!=data[page+22]||chksum[1]!=data[page+23]
+          ||chksum[2]!=data[page+24]||chksum[3]!=data[page+25]){
+        // D'oh.  Mismatch! Corrupt page (or miscapture and not a page at all)
+        // replace the computed checksum with the one actually read in
+        System.arraycopy(chksum, 0, data, page+22, 4);
+        // Bad checksum. Lose sync */
+
+        headerbytes=0;
+        bodybytes=0;
+        // search for possible capture
+        next=0;
+        for(int ii=0; ii<bytes-1; ii++){
+          if(data[page+1+ii]=='O'){
+            next=page+1+ii;
+            break;
+          }
+        }
+        //next=memchr(page+1,'O',bytes-1);
+        if(next==0)
+          next=fill;
+        returned=next;
+        return (-(next-page));
+      }
+    //}
+
+    // yes, have a whole page all ready to go
+    {
+      page=returned;
+
+      if(og!=null){
+        og.header_base=data;
+        og.header=page;
+        og.header_len=headerbytes;
+        og.body_base=data;
+        og.body=page+headerbytes;
+        og.body_len=bodybytes;
+      }
+
+      unsynced=0;
+      returned+=(bytes=headerbytes+bodybytes);
+      headerbytes=0;
+      bodybytes=0;
+      return (bytes);
+    }
+  }
+
+  // sync the stream and get a page.  Keep trying until we find a page.
+  // Supress 'sync errors' after reporting the first.
+  //
+  // return values:
+  //  -1) recapture (hole in data)
+  //   0) need more data
+  //   1) page returned
+  //
+  // Returns pointers into buffered data; invalidated by next call to
+  // _stream, _clear, _init, or _buffer
+
+  public int pageout(Page og){
+    // all we need to do is verify a page at the head of the stream
+    // buffer.  If it doesn't verify, we look for the next potential
+    // frame
+
+    while(true){
+      int ret=pageseek(og);
+      if(ret>0){
+        // have a page
+        return (1);
+      }
+      if(ret==0){
+        // need more data
+        return (0);
+      }
+
+      // head did not start a synced page... skipped some bytes
+      if(unsynced==0){
+        unsynced=1;
+        return (-1);
+      }
+      // loop. keep looking
+    }
+  }
+
+  // clear things to an initial state.  Good to call, eg, before seeking
+  public int reset(){
+    fill=0;
+    returned=0;
+    unsynced=0;
+    headerbytes=0;
+    bodybytes=0;
+    return (0);
+  }
+
+  public void init(){
+  }
+
+  public int getDataOffset(){
+    return returned;
+  }
+
+  public int getBufferOffset(){
+    return fill;
+  }
+}



More information about the commits mailing list