[xiph-commits] r17092 - in trunk: . rhea rhea/com rhea/com/jcraft rhea/com/jcraft/player rhea/com/meviatronic rhea/com/meviatronic/zeus rhea/com/meviatronic/zeus/castor rhea/com/meviatronic/zeus/helen rhea/com/meviatronic/zeus/pollux rhea/org rhea/org/xiph rhea/org/xiph/ogg
mike at svn.xiph.org
mike at svn.xiph.org
Sat Mar 27 09:16:05 PDT 2010
Author: mike
Date: 2010-03-27 09:16:05 -0700 (Sat, 27 Mar 2010)
New Revision: 17092
Added:
trunk/rhea/
trunk/rhea/README.txt
trunk/rhea/com/
trunk/rhea/com/jcraft/
trunk/rhea/com/jcraft/player/
trunk/rhea/com/jcraft/player/AudioPlayer.java
trunk/rhea/com/meviatronic/
trunk/rhea/com/meviatronic/zeus/
trunk/rhea/com/meviatronic/zeus/castor/
trunk/rhea/com/meviatronic/zeus/castor/AudioReader.java
trunk/rhea/com/meviatronic/zeus/castor/EndOfMediaException.java
trunk/rhea/com/meviatronic/zeus/castor/EndOfPacketException.java
trunk/rhea/com/meviatronic/zeus/castor/Imdct.java
trunk/rhea/com/meviatronic/zeus/castor/Output.java
trunk/rhea/com/meviatronic/zeus/castor/README.txt
trunk/rhea/com/meviatronic/zeus/castor/VorbisDecoder.java
trunk/rhea/com/meviatronic/zeus/helen/
trunk/rhea/com/meviatronic/zeus/helen/BaseTag.java
trunk/rhea/com/meviatronic/zeus/helen/Converter.java
trunk/rhea/com/meviatronic/zeus/helen/OggTag.java
trunk/rhea/com/meviatronic/zeus/helen/README.txt
trunk/rhea/com/meviatronic/zeus/helen/Tag.java
trunk/rhea/com/meviatronic/zeus/helen/TagException.java
trunk/rhea/com/meviatronic/zeus/pollux/
trunk/rhea/com/meviatronic/zeus/pollux/MemoryImageSource.java
trunk/rhea/com/meviatronic/zeus/pollux/Output.java
trunk/rhea/com/meviatronic/zeus/pollux/README.txt
trunk/rhea/com/meviatronic/zeus/pollux/TheoraDecoder.java
trunk/rhea/com/meviatronic/zeus/pollux/VideoReader.java
trunk/rhea/org/
trunk/rhea/org/xiph/
trunk/rhea/org/xiph/ogg/
trunk/rhea/org/xiph/ogg/Packet.java
trunk/rhea/org/xiph/ogg/Page.java
trunk/rhea/org/xiph/ogg/StreamState.java
trunk/rhea/org/xiph/ogg/SyncState.java
Log:
Initial import ...
Added: trunk/rhea/README.txt
===================================================================
--- trunk/rhea/README.txt (rev 0)
+++ trunk/rhea/README.txt 2010-03-27 16:16:05 UTC (rev 17092)
@@ -0,0 +1,19 @@
+ Rhea, a fast Theora/Vorbis player created by Michael Scheerer.
+ Rhea player (c) 2009 Michael Scheerer www.meviatronic.com
+
+ Many thanks to
+ Monty <monty at xiph.org> and
+ The XIPHOPHORUS Company http://www.xiph.org/ .
+
+ Rhea is intendend to be a pure Theora/Vorbis player without an underlying
+ multimedia framework. Rhea can be integrated into a Java Sound API plugin,
+ like the Vorbis plugin deployed from www.javazoom.net
+
+ Currently only the audio part together with an audio player is ready.
+ But it's still usable as a Java Sound API plugin for other open source projects like
+ the one here: http://www.javazoom.net/vorbisspi/vorbisspi.html
+
+ See:
+ https://svn.xiph.org/trunk/rhea/com/meviatronic/zeus/helen.README.txt
+ https://svn.xiph.org/trunk/rhea/com/meviatronic/zeus/castor.README.txt
+ https://svn.xiph.org/trunk/rhea/com/meviatronic/zeus/pollux.README.txt
Added: trunk/rhea/com/jcraft/player/AudioPlayer.java
===================================================================
--- trunk/rhea/com/jcraft/player/AudioPlayer.java (rev 0)
+++ trunk/rhea/com/jcraft/player/AudioPlayer.java 2010-03-27 16:16:05 UTC (rev 17092)
@@ -0,0 +1,1113 @@
+/* Audio player, created by JCraft.
+ *
+ * Audio player (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.jcraft.player;
+
+import com.meviatronic.zeus.castor.*;
+import com.meviatronic.zeus.helena.*;
+import org.xiph.ogg.*;
+
+import java.util.*;
+import java.net.*;
+import java.io.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.applet.*;
+import javax.swing.*;
+import javax.sound.sampled.*;
+
+/**
+ * @author Xiph, Michael Scheerer (Adaptions, Fixes)
+ */
+public class AudioPlayer extends JApplet implements ActionListener, Runnable{
+
+ private static final long serialVersionUID=1L;
+
+ boolean running_as_applet=true;
+
+ Thread player=null;
+ InputStream bitStream=null;
+
+ int udp_port=-1;
+ String udp_baddress=null;
+
+ static AppletContext acontext=null;
+
+ static final int BUFSIZE=4096*2;
+
+ static byte[] convbuffer;
+
+ private int RETRY=3;
+ int retry=RETRY;
+
+ String playlistfile="playlist";
+
+ boolean icestats=false;
+
+ SyncState oy;
+ StreamState os;
+ Page og;
+ Packet op;
+ AudioReader vi;
+ Output vo;
+
+ byte[] buffer=null;
+ int bytes=0;
+
+ int format;
+ int sampleRate=0;
+ int channels=0;
+ int left_vol_scale=100;
+ int right_vol_scale=100;
+ SourceDataLine outputLine=null;
+ String current_source=null;
+
+ int frameSizeInBytes;
+ int bufferLengthInBytes;
+
+ boolean playonstartup=false;
+
+ public void init(){
+ running_as_applet=true;
+
+ acontext=getAppletContext();
+
+ String s=getParameter("jorbis.player.playlist");
+ playlistfile=s;
+
+ s=getParameter("jorbis.player.icestats");
+ if(s!=null&&s.equals("yes")){
+ icestats=true;
+ }
+
+ loadPlaylist();
+ initUI();
+
+ if(playlist.size()>0){
+ s=getParameter("jorbis.player.playonstartup");
+ if(s!=null&&s.equals("yes")){
+ playonstartup=true;
+ }
+ }
+
+ setBackground(Color.lightGray);
+ // setBackground(Color.white);
+ getContentPane().setLayout(new BorderLayout());
+ getContentPane().add(panel);
+ }
+
+ public void start(){
+ super.start();
+ if(playonstartup){
+ play_sound();
+ }
+ }
+
+ void init_jorbis(){
+ oy=new SyncState();
+ os=new StreamState();
+ og=new Page();
+ op=new Packet();
+ vi=new AudioReader();
+
+ buffer=null;
+ bytes=0;
+ oy.init();
+ }
+
+ SourceDataLine getOutputLine(int channels, int sampleRate){
+ if(outputLine==null||this.sampleRate!=sampleRate||this.channels!=channels){
+ if(outputLine!=null){
+ outputLine.drain();
+ outputLine.stop();
+ outputLine.close();
+ }
+ init_audio(channels, sampleRate);
+ outputLine.start();
+ }
+ return outputLine;
+ }
+
+ void init_audio(int channels, int sampleRate){
+ try{
+ //ClassLoader originalClassLoader=null;
+ //try{
+ // originalClassLoader=Thread.currentThread().getContextClassLoader();
+ // Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());
+ //}
+ //catch(Exception ee){
+ // System.out.println(ee);
+ //}
+ AudioFormat audioFormat=new AudioFormat((float)sampleRate, 16, channels, true, // PCM_Signed
+ false // littleEndian
+ );
+ DataLine.Info info=new DataLine.Info(SourceDataLine.class, audioFormat, AudioSystem.NOT_SPECIFIED);
+ if(!AudioSystem.isLineSupported(info)){
+ //System.out.println("Line " + info + " not supported.");
+ return;
+ }
+
+ try{
+ outputLine=(SourceDataLine)AudioSystem.getLine(info);
+ //outputLine.addLineListener(this);
+ outputLine.open(audioFormat);
+ }
+ catch(LineUnavailableException ex){
+ System.out.println("Unable to open the sourceDataLine: "+ex);
+ return;
+ }
+ catch(IllegalArgumentException ex){
+ System.out.println("Illegal Argument: "+ex);
+ return;
+ }
+
+ frameSizeInBytes=audioFormat.getFrameSize();
+ int bufferLengthInFrames=outputLine.getBufferSize()/frameSizeInBytes/2;
+ bufferLengthInBytes=bufferLengthInFrames*frameSizeInBytes;
+
+ //if(originalClassLoader!=null)
+ // Thread.currentThread().setContextClassLoader(originalClassLoader);
+
+ this.sampleRate=sampleRate;
+ this.channels=channels;
+ }
+ catch(Exception ee){
+ System.out.println(ee);
+ }
+ }
+
+ private int item2index(String item){
+ for(int i=0; i<cb.getItemCount(); i++){
+ String foo=(String)(cb.getItemAt(i));
+ if(item.equals(foo))
+ return i;
+ }
+ cb.addItem(item);
+ return cb.getItemCount()-1;
+ }
+
+ public synchronized void run(){
+ Thread me=Thread.currentThread();
+ String item=(String)(cb.getSelectedItem());
+ int current_index=item2index(item);
+ while(true){
+ item=(String)(cb.getItemAt(current_index));
+ cb.setSelectedIndex(current_index);
+ bitStream=selectSource(item);
+ if(bitStream!=null){
+ if(udp_port!=-1){
+ play_udp_stream(me);
+ }
+ else{
+ play_stream(me,item);
+ }
+ }
+ else if(cb.getItemCount()==1){
+ break;
+ }
+ if(player!=me){
+ break;
+ }
+
+ current_index++;
+ if(current_index>=cb.getItemCount()){
+ current_index=0;
+ }
+ if(cb.getItemCount()<=0)
+ break;
+ }
+
+ start_button.setText("start");
+ stop();
+ }
+
+ private void play_stream(Thread me,String item){
+
+ boolean chained=false;
+
+ init_jorbis();
+
+ retry=RETRY;
+
+ //System.out.println("play_stream>");
+
+ loop: while(true){
+ int eos=0;
+
+ int index=oy.buffer(BUFSIZE);
+ buffer=oy.data;
+ try{
+ bytes=bitStream.read(buffer, index, BUFSIZE);
+ }
+ catch(Exception e){
+ System.err.println(e+" "+buffer+" "+bitStream);
+ return;
+ }
+ oy.wrote(bytes);
+
+ if(chained){ //
+ chained=false; //
+ } //
+ else{ //
+ if(oy.pageout(og)!=1){
+ if(bytes<BUFSIZE)
+ break;
+ System.err.println("Input does not appear to be an Ogg bitstream.");
+ return;
+ }
+ } //
+ os.init(og.serialno());
+ os.reset();
+
+ if(os.pagein(og)<0){
+ // error; stream version mismatch perhaps
+ System.err.println("Error reading first page of Ogg bitstream data.");
+ return;
+ }
+
+ retry=RETRY;
+
+ if(os.packetout(op)!=1){
+ // no page? must not be vorbis
+ System.out.println("Error reading initial header packet.");
+ break;
+ // return;
+ }
+
+ try {
+ vi.readMediaInformation(op);
+ } catch (Exception e) {
+ System.out.println("This Ogg bitstream does not contain Vorbis audio data.");
+ stop();
+ return;
+ }
+
+
+ int i=0;
+
+ while(i<2){
+ while(i<2){
+ int result=oy.pageout(og);
+ if(result==0)
+ break; // Need more data
+ if(result==1){
+ os.pagein(og);
+ while(i<2){
+ result=os.packetout(op);
+ if(result==0)
+ break;
+ if(result==-1){
+ System.out.println("Corrupt secondary header. Exiting.");
+ //return;
+ break loop;
+ }
+ try {
+ vi.readMediaInformation(op);
+ } catch (Exception e) {
+ stop();
+ }
+ i++;
+ }
+ }
+ }
+
+ index=oy.buffer(BUFSIZE);
+ buffer=oy.data;
+ try{
+ bytes=bitStream.read(buffer, index, BUFSIZE);
+ }
+ catch(Exception e){
+ System.err.println(e);
+ return;
+ }
+ if(bytes==0&&i<2){
+ System.err.println("End of file before finding all Vorbis headers!");
+ return;
+ }
+ oy.wrote(bytes);
+ }
+
+
+ System.out.println(vi.getOggCommentContent());
+ if (acontext != null) {
+ acontext.showStatus(vi.getOggCommentContent());
+ }
+
+ if (vo == null) {
+ vo = new VorbisDecoder(vi);
+ }
+
+ float[][][] pcmf = new float [1][][];
+ int[] _index=new int[vi.channels];
+
+ getOutputLine(vi.channels, vi.sampleRate);
+
+ while(eos==0){
+ while(eos==0){
+
+ if(player!=me){
+ stop();
+ return;
+ }
+
+ int result=oy.pageout(og);
+ if(result==0)
+ break; // need more data
+ if(result==-1){ // missing or corrupt data at this page position
+ // System.err.println("Corrupt or missing data in bitstream; continuing...");
+ }
+ else{
+ os.pagein(og);
+
+ if(og.granulepos()==0){ //
+ chained=true; //
+ eos=1; //
+ break; //
+ } //
+
+ while(true){
+ result=os.packetout(op);
+ if(result==0)
+ break; // need more data
+ if(result==-1){ // missing or corrupt data at this page position
+ // no reason to complain; already complained above
+
+ //System.err.println("no reason to complain; already complained above");
+ }
+ else{
+ // we have a packet. Decode it
+
+ try {
+ convbuffer = vo.synthesis(op);
+ } catch (Exception e) {
+
+ }
+ outputLine.write(convbuffer, 2 * vo.getSampleOffset(), 2 * vo.getNumberOfSamples());
+ }
+ }
+ if(og.eos()!=0)
+ eos=1;
+ }
+ }
+
+ if(eos==0){
+ index=oy.buffer(BUFSIZE);
+ buffer=oy.data;
+ try{
+ bytes=bitStream.read(buffer, index, BUFSIZE);
+ }
+ catch(Exception e){
+ System.err.println(e);
+ return;
+ }
+ if(bytes==-1){
+ break;
+ }
+ oy.wrote(bytes);
+ if(bytes==0)
+ eos=1;
+ }
+ }
+ }
+ stop();
+ }
+
+ private void play_udp_stream(Thread me){
+ init_jorbis();
+
+ try{
+ loop: while(true){
+ int index=oy.buffer(BUFSIZE);
+ buffer=oy.data;
+ try{
+ bytes=bitStream.read(buffer, index, BUFSIZE);
+ }
+ catch(Exception e){
+ System.err.println(e);
+ return;
+ }
+
+ oy.wrote(bytes);
+ if(oy.pageout(og)!=1){
+ // if(bytes<BUFSIZE)break;
+ System.err.println("Input does not appear to be an Ogg bitstream.");
+ return;
+ }
+
+ os.init(og.serialno());
+ os.reset();
+
+ if(os.pagein(og)<0){
+ // error; stream version mismatch perhaps
+ System.err.println("Error reading first page of Ogg bitstream data.");
+ return;
+ }
+
+ if(os.packetout(op)!=1){
+ // no page? must not be vorbis
+ System.err.println("Error reading initial header packet.");
+ // break;
+ return;
+ }
+
+ try {
+ vi.readMediaInformation(op);
+ } catch (Exception e) {
+ stop();
+ }
+
+
+ int i=0;
+ while(i<2){
+ while(i<2){
+ int result=oy.pageout(og);
+ if(result==0)
+ break; // Need more data
+ if(result==1){
+ os.pagein(og);
+ while(i<2){
+ result=os.packetout(op);
+ if(result==0)
+ break;
+ if(result==-1){
+ System.err.println("Corrupt secondary header. Exiting.");
+ //return;
+ break loop;
+ }
+
+ try {
+ vi.readMediaInformation(op);
+ } catch (Exception e) {
+ stop();
+ }
+ i++;
+ }
+ }
+ }
+
+ if(i==2)
+ break;
+
+ index=oy.buffer(BUFSIZE);
+ buffer=oy.data;
+ try{
+ bytes=bitStream.read(buffer, index, BUFSIZE);
+ }
+ catch(Exception e){
+ stop();
+ System.err.println(e);
+ return;
+ }
+ if(bytes==0&&i<2){
+ stop();
+ System.err
+ .println("End of file before finding all Vorbis headers!");
+ return;
+ }
+ oy.wrote(bytes);
+ }
+ break;
+ }
+ }
+ catch(Exception e){
+ }
+
+ stop();
+
+ UDPIO io=null;
+ io=new UDPIO(udp_port);
+
+
+ bitStream=io;
+ play_stream(me,null);
+ }
+
+ public synchronized void stop(){
+ if (os != null) {
+ os.close();
+ }
+ if (vo != null) {
+ vo.close();
+ }
+ if (vi != null) {
+ vi.close();
+ }
+ if (oy != null) {
+ oy.close();
+ }
+ if (og != null) {
+ og.close();
+ }
+ if (outputLine != null) {
+ outputLine.drain();
+ outputLine.stop();
+ outputLine.close();
+ outputLine = null;
+ }
+ try {
+ if(bitStream != null) {
+ bitStream.close();
+ }
+ bitStream = null;
+ } catch (Exception e) {
+
+ }
+
+ os = null;
+ vo = null;
+ vi = null;
+ oy = null;
+ og = null;
+ buffer = null;
+ }
+
+ Vector playlist=new Vector();
+
+ public void actionPerformed(ActionEvent e){
+
+ if(e.getSource()==stats_button){
+ String item=(String)(cb.getSelectedItem());
+ if(!item.startsWith("http://"))
+ return;
+ if(item.endsWith(".pls")){
+ item=fetch_pls(item);
+ if(item==null)
+ return;
+ }
+ else if(item.endsWith(".m3u")){
+ item=fetch_m3u(item);
+ if(item==null)
+ return;
+ }
+ byte[] foo=item.getBytes();
+ for(int i=foo.length-1; i>=0; i--){
+ if(foo[i]=='/'){
+ item=item.substring(0, i+1)+"stats.xml";
+ break;
+ }
+ }
+ System.out.println(item);
+ try{
+ URL url=null;
+ if(running_as_applet)
+ url=new URL(getCodeBase(), item);
+ else
+ url=new URL(item);
+ BufferedReader stats=new BufferedReader(new InputStreamReader(url
+ .openConnection().getInputStream()));
+ while(true){
+ String bar=stats.readLine();
+ if(bar==null)
+ break;
+ System.out.println(bar);
+ }
+ }
+ catch(Exception ee){
+ System.err.println(ee);
+ }
+ return;
+ }
+
+ String command=((JButton)(e.getSource())).getText();
+ if(command.equals("start")&&player==null){
+ play_sound();
+ }
+ else if(player!=null){
+ stop_sound();
+ }
+ }
+
+ public String getTitle(){
+ return (String)(cb.getSelectedItem());
+ }
+
+ public void play_sound(){
+ if(player!=null)
+ return;
+ player=new Thread(this);
+ start_button.setText("stop");
+ player.start();
+ }
+
+ public void stop_sound(){
+ if(player==null)
+ return;
+ player=null;
+ start_button.setText("start");
+ }
+
+ InputStream selectSource(String item){
+ if(item.endsWith(".pls")){
+ item=fetch_pls(item);
+ if(item==null)
+ return null;
+ //System.out.println("fetch: "+item);
+ }
+ else if(item.endsWith(".m3u")){
+ item=fetch_m3u(item);
+ if(item==null)
+ return null;
+ //System.out.println("fetch: "+item);
+ }
+
+ if(!item.endsWith(".ogg")){
+ return null;
+ }
+
+ InputStream is=null;
+ URLConnection urlc=null;
+ try{
+ URL url=null;
+ if(running_as_applet)
+ url=new URL(getCodeBase(), item);
+ else
+ url=new URL(item);
+ urlc=url.openConnection();
+ is=urlc.getInputStream();
+ current_source=url.getProtocol()+"://"+url.getHost()+":"+url.getPort()
+ +url.getFile();
+ }
+ catch(Exception ee){
+ //System.err.println(ee);
+ }
+
+ if(is==null&&!running_as_applet){
+ try{
+ is=new FileInputStream(System.getProperty("user.dir")
+ +System.getProperty("file.separator")+item);
+ current_source=null;
+ }
+ catch(Exception ee){
+ //System.err.println(ee);
+ }
+ }
+
+ if(is==null)
+ return null;
+
+ //System.out.println("Select: "+item);
+
+ {
+ boolean find=false;
+ for(int i=0; i<cb.getItemCount(); i++){
+ String foo=(String)(cb.getItemAt(i));
+ if(item.equals(foo)){
+ find=true;
+ break;
+ }
+ }
+ if(!find){
+ cb.addItem(item);
+ }
+ }
+
+ int i=0;
+ String s=null;
+ String t=null;
+ udp_port=-1;
+ udp_baddress=null;
+ while(urlc!=null&&true){
+ s=urlc.getHeaderField(i);
+ t=urlc.getHeaderFieldKey(i);
+ if(s==null)
+ break;
+ i++;
+ if(t!=null&&t.equals("udp-port")){
+ try{
+ udp_port=Integer.parseInt(s);
+ }
+ catch(Exception ee){
+ System.err.println(ee);
+ }
+ }
+ else if(t!=null&&t.equals("udp-broadcast-address")){
+ udp_baddress=s;
+ }
+ }
+ return is;
+ }
+
+ String fetch_pls(String pls){
+ InputStream pstream=null;
+ if(pls.startsWith("http://")){
+ try{
+ URL url=null;
+ if(running_as_applet)
+ url=new URL(getCodeBase(), pls);
+ else
+ url=new URL(pls);
+ URLConnection urlc=url.openConnection();
+ pstream=urlc.getInputStream();
+ }
+ catch(Exception ee){
+ System.err.println(ee);
+ return null;
+ }
+ }
+ if(pstream==null&&!running_as_applet){
+ try{
+ pstream=new FileInputStream(System.getProperty("user.dir")
+ +System.getProperty("file.separator")+pls);
+ }
+ catch(Exception ee){
+ System.err.println(ee);
+ return null;
+ }
+ }
+
+ String line=null;
+ while(true){
+ try{
+ line=readline(pstream);
+ }
+ catch(Exception e){
+ }
+ if(line==null)
+ break;
+ if(line.startsWith("File1=")){
+ byte[] foo=line.getBytes();
+ int i=6;
+ for(; i<foo.length; i++){
+ if(foo[i]==0x0d)
+ break;
+ }
+ return line.substring(6, i);
+ }
+ }
+ return null;
+ }
+
+ String fetch_m3u(String m3u){
+ InputStream pstream=null;
+ if(m3u.startsWith("http://")){
+ try{
+ URL url=null;
+ if(running_as_applet)
+ url=new URL(getCodeBase(), m3u);
+ else
+ url=new URL(m3u);
+ URLConnection urlc=url.openConnection();
+ pstream=urlc.getInputStream();
+ }
+ catch(Exception ee){
+ System.err.println(ee);
+ return null;
+ }
+ }
+ if(pstream==null&&!running_as_applet){
+ try{
+ pstream=new FileInputStream(System.getProperty("user.dir")
+ +System.getProperty("file.separator")+m3u);
+ }
+ catch(Exception ee){
+ System.err.println(ee);
+ return null;
+ }
+ }
+
+ String line=null;
+ while(true){
+ try{
+ line=readline(pstream);
+ }
+ catch(Exception e){
+ }
+ if(line==null)
+ break;
+ return line;
+ }
+ return null;
+ }
+
+ void loadPlaylist(){
+
+ if(running_as_applet){
+ String s=null;
+ for(int i=0; i<10; i++){
+ s=getParameter("jorbis.player.play."+i);
+ if(s==null)
+ break;
+ playlist.addElement(s);
+ }
+ }
+
+ if(playlistfile==null){
+ return;
+ }
+
+ try{
+ InputStream is=null;
+ try{
+ URL url=null;
+ if(running_as_applet)
+ url=new URL(getCodeBase(), playlistfile);
+ else
+ url=new URL(playlistfile);
+ URLConnection urlc=url.openConnection();
+ is=urlc.getInputStream();
+ }
+ catch(Exception ee){
+ }
+ if(is==null&&!running_as_applet){
+ try{
+ is=new FileInputStream(System.getProperty("user.dir")
+ +System.getProperty("file.separator")+playlistfile);
+ }
+ catch(Exception ee){
+ }
+ }
+
+ if(is==null)
+ return;
+
+ while(true){
+ String line=readline(is);
+ if(line==null)
+ break;
+ byte[] foo=line.getBytes();
+ for(int i=0; i<foo.length; i++){
+ if(foo[i]==0x0d){
+ line=new String(foo, 0, i);
+ break;
+ }
+ }
+ playlist.addElement(line);
+ }
+ }
+ catch(Exception e){
+ System.out.println(e);
+ }
+ }
+
+ private String readline(InputStream is){
+ StringBuffer rtn=new StringBuffer();
+ int temp;
+ do{
+ try{
+ temp=is.read();
+ }
+ catch(Exception e){
+ return (null);
+ }
+ if(temp==-1){
+ String str=rtn.toString();
+ if(str.length()==0)
+ return (null);
+ return str;
+ }
+ if(temp!=0&&temp!='\n'&&temp!='\r')
+ rtn.append((char)temp);
+ }
+ while(temp!='\n'&&temp!='\r');
+ return (rtn.toString());
+ }
+
+ JPanel panel;
+ JComboBox cb;
+ JButton start_button;
+ JButton stats_button;
+
+ void initUI(){
+ panel=new JPanel();
+
+ cb=new JComboBox(playlist);
+ cb.setEditable(true);
+ panel.add(cb);
+
+ start_button=new JButton("start");
+ start_button.addActionListener(this);
+ panel.add(start_button);
+
+ if(icestats){
+ stats_button=new JButton("IceStats");
+ stats_button.addActionListener(this);
+ panel.add(stats_button);
+ }
+ }
+
+ class UDPIO extends InputStream {
+ InetAddress address;
+ DatagramSocket socket=null;
+ DatagramPacket sndpacket;
+ DatagramPacket recpacket;
+ byte[] buf=new byte[1024];
+ //String host;
+ int port;
+ byte[] inbuffer=new byte[2048];
+ byte[] outbuffer=new byte[1024];
+ int instart=0, inend=0, outindex=0;
+
+ UDPIO(int port){
+ this.port=port;
+ try{
+ socket=new DatagramSocket(port);
+ }
+ catch(Exception e){
+ System.err.println(e);
+ }
+ recpacket=new DatagramPacket(buf, 1024);
+ }
+
+ void setTimeout(int i){
+ try{
+ socket.setSoTimeout(i);
+ }
+ catch(Exception e){
+ System.out.println(e);
+ }
+ }
+
+ int getByte() throws java.io.IOException{
+ if((inend-instart)<1){
+ read(1);
+ }
+ return inbuffer[instart++]&0xff;
+ }
+
+ int getByte(byte[] array) throws java.io.IOException{
+ return getByte(array, 0, array.length);
+ }
+
+ int getByte(byte[] array, int begin, int length) throws java.io.IOException{
+ int i=0;
+ int foo=begin;
+ while(true){
+ if((i=(inend-instart))<length){
+ if(i!=0){
+ System.arraycopy(inbuffer, instart, array, begin, i);
+ begin+=i;
+ length-=i;
+ instart+=i;
+ }
+ read(length);
+ continue;
+ }
+ System.arraycopy(inbuffer, instart, array, begin, length);
+ instart+=length;
+ break;
+ }
+ return begin+length-foo;
+ }
+
+ int getShort() throws java.io.IOException{
+ if((inend-instart)<2){
+ read(2);
+ }
+ int s=0;
+ s=inbuffer[instart++]&0xff;
+ s=((s<<8)&0xffff)|(inbuffer[instart++]&0xff);
+ return s;
+ }
+
+ int getInt() throws java.io.IOException{
+ if((inend-instart)<4){
+ read(4);
+ }
+ int i=0;
+ i=inbuffer[instart++]&0xff;
+ i=((i<<8)&0xffff)|(inbuffer[instart++]&0xff);
+ i=((i<<8)&0xffffff)|(inbuffer[instart++]&0xff);
+ i=(i<<8)|(inbuffer[instart++]&0xff);
+ return i;
+ }
+
+ void getPad(int n) throws java.io.IOException{
+ int i;
+ while(n>0){
+ if((i=inend-instart)<n){
+ n-=i;
+ instart+=i;
+ read(n);
+ continue;
+ }
+ instart+=n;
+ break;
+ }
+ }
+
+ void read(int n) throws java.io.IOException{
+ if(n>inbuffer.length){
+ n=inbuffer.length;
+ }
+ instart=inend=0;
+ int i;
+ while(true){
+ recpacket.setData(buf, 0, 1024);
+ socket.receive(recpacket);
+
+ i=recpacket.getLength();
+ System.arraycopy(recpacket.getData(), 0, inbuffer, inend, i);
+ if(i==-1){
+ throw new java.io.IOException();
+ }
+ inend+=i;
+ break;
+ }
+ }
+
+
+ public void close() throws java.io.IOException{
+ socket.close();
+ }
+
+ public int read() throws java.io.IOException{
+ return 0;
+ }
+
+ public int read(byte[] array, int begin, int length)
+ throws java.io.IOException{
+ return getByte(array, begin, length);
+ }
+ }
+
+ public static void main(String[] arg){
+
+ JFrame frame=new JFrame("AudioPlayer");
+ frame.setBackground(Color.lightGray);
+ frame.setBackground(Color.white);
+ frame.getContentPane().setLayout(new BorderLayout());
+
+ frame.addWindowListener(new WindowAdapter(){
+ public void windowClosing(WindowEvent e){
+ System.exit(0);
+ }
+ });
+
+ AudioPlayer player=new AudioPlayer();
+ player.running_as_applet=false;
+
+ if(arg.length>0){
+ for(int i=0; i<arg.length; i++){
+ player.playlist.addElement(arg[i]);
+ }
+ }
+
+ player.loadPlaylist();
+ player.initUI();
+
+ frame.getContentPane().add(player.panel);
+ frame.pack();
+ frame.setVisible(true);
+ }
+}
Added: trunk/rhea/com/meviatronic/zeus/castor/AudioReader.java
===================================================================
--- trunk/rhea/com/meviatronic/zeus/castor/AudioReader.java (rev 0)
+++ trunk/rhea/com/meviatronic/zeus/castor/AudioReader.java 2010-03-27 16:16:05 UTC (rev 17092)
@@ -0,0 +1,1036 @@
+/* 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;
+
+ public int version;
+ public int channels;
+ public int sampleRate;
+ public int bitRateMax ;
+ public int bitRate;
+ public int bitRateMin;
+
+ private int byteIdx;
+ private byte[] data;
+ private int revBitIdx;
+ private int packetByteIdx;
+ private int packetSize;
+
+ // Vorbis extendet header
+ private HuffTreeEntry[] hts;
+ private int[] codebookCodewordLengths;
+
+ int[] codebookDimensions;
+ float[][] valueVector;
+ int[] blocksizes = new int[2];
+ int[] vorbisFloorTypes;
+ int[] floor1Multiplieres;
+ int[][] floor1PartitionClassList;
+ int[][] floor1ClassDimensions;
+ int[][] floor1ClassSubclasses;
+ int[][] floor1ClassMasterbooks;
+ int[][] floor1XList;
+ int floor1MaximumValues;
+ int[][][] floor1SubclassBooks;
+ int[] vorbisResidueTypes;
+ int[] residueMaximumPasses;
+ int[] residueBegin;
+ int[] residueEnd;
+ int[] residuePartitionSize;
+ int[] residueClassifications;
+ int[] residueClassbooks;
+ int[][] residueCascade;
+ int[][][] residueBooks;
+ int[][] vorbisMappingMagnitude;
+ int[][] vorbisMappingAngle;
+ int[][] vorbisMappingMux;
+ int[][] vorbisMappingSubmapFloor;
+ int[][] vorbisMappingSubmapResidue;
+ int[] vorbisModeBlockflag;
+ int[] vorbisModeWindowtype;
+ int[] vorbisModeTransformtype;
+ int[] vorbisModeMapping;
+ int modeNumberBits;
+
+ private Tag tag;
+
+ private boolean vbr;
+
+ 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;
+ }
+ }
+ }
+
+ 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());
+ }
+ }
+ }
+
+ 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");
+ }
+ }
+
+ 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");
+ }
+ }
+
+ /**
+ * 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;
+ blocksizes = 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;
+ }
+
+ 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/rhea/com/meviatronic/zeus/castor/EndOfMediaException.java
===================================================================
--- trunk/rhea/com/meviatronic/zeus/castor/EndOfMediaException.java (rev 0)
+++ trunk/rhea/com/meviatronic/zeus/castor/EndOfMediaException.java 2010-03-27 16:16:05 UTC (rev 17092)
@@ -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/rhea/com/meviatronic/zeus/castor/EndOfPacketException.java
===================================================================
--- trunk/rhea/com/meviatronic/zeus/castor/EndOfPacketException.java (rev 0)
+++ trunk/rhea/com/meviatronic/zeus/castor/EndOfPacketException.java 2010-03-27 16:16:05 UTC (rev 17092)
@@ -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/rhea/com/meviatronic/zeus/castor/Imdct.java
===================================================================
--- trunk/rhea/com/meviatronic/zeus/castor/Imdct.java (rev 0)
+++ trunk/rhea/com/meviatronic/zeus/castor/Imdct.java 2010-03-27 16:16:05 UTC (rev 17092)
@@ -0,0 +1,301 @@
+/* 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;
+ i2Plus1 = i2 + 1;
+ C[i2] = (float) Math.cos(Math.PI / nHalf * (i << 1 | 1));
+ C[i2Plus1] = (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/rhea/com/meviatronic/zeus/castor/Output.java
===================================================================
--- trunk/rhea/com/meviatronic/zeus/castor/Output.java (rev 0)
+++ trunk/rhea/com/meviatronic/zeus/castor/Output.java 2010-03-27 16:16:05 UTC (rev 17092)
@@ -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 little-endian order to 2 bytes in big-endian order!!
+ byte b2 = (byte) (w >>> 8);
+ byte b1 = (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/rhea/com/meviatronic/zeus/castor/README.txt
===================================================================
--- trunk/rhea/com/meviatronic/zeus/castor/README.txt (rev 0)
+++ trunk/rhea/com/meviatronic/zeus/castor/README.txt 2010-03-27 16:16:05 UTC (rev 17092)
@@ -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/rhea/com/meviatronic/zeus/castor/VorbisDecoder.java
===================================================================
--- trunk/rhea/com/meviatronic/zeus/castor/VorbisDecoder.java (rev 0)
+++ trunk/rhea/com/meviatronic/zeus/castor/VorbisDecoder.java 2010-03-27 16:16:05 UTC (rev 17092)
@@ -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/rhea/com/meviatronic/zeus/helen/BaseTag.java
===================================================================
--- trunk/rhea/com/meviatronic/zeus/helen/BaseTag.java (rev 0)
+++ trunk/rhea/com/meviatronic/zeus/helen/BaseTag.java 2010-03-27 16:16:05 UTC (rev 17092)
@@ -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/rhea/com/meviatronic/zeus/helen/Converter.java
===================================================================
--- trunk/rhea/com/meviatronic/zeus/helen/Converter.java (rev 0)
+++ trunk/rhea/com/meviatronic/zeus/helen/Converter.java 2010-03-27 16:16:05 UTC (rev 17092)
@@ -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/rhea/com/meviatronic/zeus/helen/OggTag.java
===================================================================
--- trunk/rhea/com/meviatronic/zeus/helen/OggTag.java (rev 0)
+++ trunk/rhea/com/meviatronic/zeus/helen/OggTag.java 2010-03-27 16:16:05 UTC (rev 17092)
@@ -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/rhea/com/meviatronic/zeus/helen/README.txt
===================================================================
--- trunk/rhea/com/meviatronic/zeus/helen/README.txt (rev 0)
+++ trunk/rhea/com/meviatronic/zeus/helen/README.txt 2010-03-27 16:16:05 UTC (rev 17092)
@@ -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/rhea/com/meviatronic/zeus/helen/Tag.java
===================================================================
--- trunk/rhea/com/meviatronic/zeus/helen/Tag.java (rev 0)
+++ trunk/rhea/com/meviatronic/zeus/helen/Tag.java 2010-03-27 16:16:05 UTC (rev 17092)
@@ -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/rhea/com/meviatronic/zeus/helen/TagException.java
===================================================================
--- trunk/rhea/com/meviatronic/zeus/helen/TagException.java (rev 0)
+++ trunk/rhea/com/meviatronic/zeus/helen/TagException.java 2010-03-27 16:16:05 UTC (rev 17092)
@@ -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/rhea/com/meviatronic/zeus/pollux/MemoryImageSource.java
===================================================================
--- trunk/rhea/com/meviatronic/zeus/pollux/MemoryImageSource.java (rev 0)
+++ trunk/rhea/com/meviatronic/zeus/pollux/MemoryImageSource.java 2010-03-27 16:16:05 UTC (rev 17092)
@@ -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/rhea/com/meviatronic/zeus/pollux/Output.java
===================================================================
--- trunk/rhea/com/meviatronic/zeus/pollux/Output.java (rev 0)
+++ trunk/rhea/com/meviatronic/zeus/pollux/Output.java 2010-03-27 16:16:05 UTC (rev 17092)
@@ -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/rhea/com/meviatronic/zeus/pollux/README.txt
===================================================================
--- trunk/rhea/com/meviatronic/zeus/pollux/README.txt (rev 0)
+++ trunk/rhea/com/meviatronic/zeus/pollux/README.txt 2010-03-27 16:16:05 UTC (rev 17092)
@@ -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/rhea/com/meviatronic/zeus/pollux/TheoraDecoder.java
===================================================================
--- trunk/rhea/com/meviatronic/zeus/pollux/TheoraDecoder.java (rev 0)
+++ trunk/rhea/com/meviatronic/zeus/pollux/TheoraDecoder.java 2010-03-27 16:16:05 UTC (rev 17092)
@@ -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/rhea/com/meviatronic/zeus/pollux/VideoReader.java
===================================================================
--- trunk/rhea/com/meviatronic/zeus/pollux/VideoReader.java (rev 0)
+++ trunk/rhea/com/meviatronic/zeus/pollux/VideoReader.java 2010-03-27 16:16:05 UTC (rev 17092)
@@ -0,0 +1,702 @@
+/* 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 int byteIdx;
+ private byte[] data;
+ private int bitIdx;
+ private int packetByteIdx;
+ private int packetSize;
+
+ // Theora header
+ int mbWidth;
+ int mbHeight;
+ int sbChromaWidth;
+ int sbChromaHeight;
+ int sbLumaWidth;
+ int sbLumaHeight;
+ public int codedPictureWidth;
+ public int codedPictureHeight;
+ public int pictureRegionW;
+ public int pictureRegionH;
+ public int pictureRegionX;
+ public int pictureRegionY;
+ private int colorSpace;
+ private int videoBitrate;
+ private int qualityHint;
+ int keyFrameNumberGranuleShift;
+ int chromaFormat;
+ public Dimension aspectRatio;
+ public double frameRate;
+ int macroblocks;
+ int superblocks;
+ int blocks;
+ private int versionRevisionNumber;
+ // Theora extended header
+ private short[] acScale = new short[64]; // A 64-element array of scale
+ private short[] dcScale = new short[64];
+ short [][][][] qmat = new short[2][3][64][64]; // A 64-element array of quantization values for each DCT coefficient in natural order.
+
+ private short[][] bms; // A NBMS/64 array containing the base matrices.
+ private 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 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 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 HuffTreeEntry[] hts = new HuffTreeEntry[80]; // An 80-element array of Huffman tables
+ // with up to 32 entries each.
+ private int entries;
+
+ byte[] lflims = new byte[64];
+
+ private Tag tag;
+
+ 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;
+ }
+
+ 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());
+ }
+ }
+ }
+
+ 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);
+ }
+ }
+ }
+ }
+ // 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) {
+ verifyFirstPacket();
+ } else if (packetType == 0x81 && op.bos == 0) {
+ verifySecondPacket();
+ } else if (packetType == 0x82 && op.bos == 0) {
+ verifyThirdPacket();
+ } else {
+ throw new InterruptedIOException("Wrong packet order");
+ }
+ }
+
+ 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];
+
+ 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 != null) {
+ for (i = 0; i < 80; i++) {
+ closeTree(hts[i]);
+ hts[i] = null;
+ }
+ hts = null;
+ } else {
+ return;
+ }
+
+ tag.close();
+ tag = null;
+
+ data = null;
+ lflims = null;
+ acScale = null;
+ dcScale = null;
+ for (i = 0; i < bms.length; i++) {
+ bms[i] = null;
+ }
+ bms = null;
+ for (i = 0; i < nqrs.length; i++) {
+ nqrs[i] = null;
+ }
+ nqrs = null;
+
+ short[][] pointer;
+
+ for (i = 0; i < qrSizes.length; i++) {
+ pointer = qrSizes[i];
+ for (j = 0; j < pointer.length; j++) {
+ pointer[j] = null;
+ }
+ pointer = null;
+ }
+ qrSizes = null;
+ for (i = 0; i < qrbmis.length; i++) {
+ pointer = qrbmis[i];
+ for (j = 0; j < pointer.length; j++) {
+ pointer[j] = null;
+ }
+ pointer = null;
+ }
+ qrbmis = null;
+
+ short[][][] qmatOuterPointer;
+ short[][] qmatInnerPointer;
+
+ for (i = 0; i < qmat.length; i++) {
+ qmatOuterPointer = qmat[i];
+ for (j = 0; j < qmatOuterPointer.length; j++) {
+ qmatInnerPointer = qmatOuterPointer[j];
+ for (k = 0; k < qmatInnerPointer.length; k++) {
+ qmatInnerPointer[k] = null;
+ }
+ qmatInnerPointer = null;
+ }
+ qmatOuterPointer = null;
+ }
+ qmat = 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) {
+ 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/rhea/org/xiph/ogg/Packet.java
===================================================================
--- trunk/rhea/org/xiph/ogg/Packet.java (rev 0)
+++ trunk/rhea/org/xiph/ogg/Packet.java 2010-03-27 16:16:05 UTC (rev 17092)
@@ -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/rhea/org/xiph/ogg/Page.java
===================================================================
--- trunk/rhea/org/xiph/ogg/Page.java (rev 0)
+++ trunk/rhea/org/xiph/ogg/Page.java 2010-03-27 16:16:05 UTC (rev 17092)
@@ -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/rhea/org/xiph/ogg/StreamState.java
===================================================================
--- trunk/rhea/org/xiph/ogg/StreamState.java (rev 0)
+++ trunk/rhea/org/xiph/ogg/StreamState.java 2010-03-27 16:16:05 UTC (rev 17092)
@@ -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/rhea/org/xiph/ogg/SyncState.java
===================================================================
--- trunk/rhea/org/xiph/ogg/SyncState.java (rev 0)
+++ trunk/rhea/org/xiph/ogg/SyncState.java 2010-03-27 16:16:05 UTC (rev 17092)
@@ -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