[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
+ // bmis 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