summaryrefslogtreecommitdiffstats
path: root/songdbj/org/tritonus/lowlevel/pogg/StreamState.java
diff options
context:
space:
mode:
Diffstat (limited to 'songdbj/org/tritonus/lowlevel/pogg/StreamState.java')
-rw-r--r--songdbj/org/tritonus/lowlevel/pogg/StreamState.java703
1 files changed, 703 insertions, 0 deletions
diff --git a/songdbj/org/tritonus/lowlevel/pogg/StreamState.java b/songdbj/org/tritonus/lowlevel/pogg/StreamState.java
new file mode 100644
index 0000000000..3fde33de8f
--- /dev/null
+++ b/songdbj/org/tritonus/lowlevel/pogg/StreamState.java
@@ -0,0 +1,703 @@
+/*
+ * StreamState.java
+ *
+ * This file is part of Tritonus: http://www.tritonus.org/
+ */
+
+/*
+ * Copyright (c) 2000 - 2005 by Matthias Pfisterer
+ *
+ * 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.
+ */
+
+/*
+|<--- this code is formatted to fit into 80 columns --->|
+*/
+
+package org.tritonus.lowlevel.pogg;
+
+import org.tritonus.share.TDebug;
+
+
+/** Wrapper for ogg_stream_state.
+ */
+public class StreamState
+{
+ private static final int INITIAL_BODY_DATA_SIZE = 16 * 1024;
+ private static final int INITIAL_LACING_VALUES_SIZE = 1024;
+
+ /** The serial number of the stream.
+ This is set by init().
+ */
+ private int m_nSerialNo;
+
+ /** Storage for packet bodies.
+ */
+ private byte[] m_abBodyData;
+
+ /** Number of bytes used in te body storage.
+ */
+ private int m_nBodyFill;
+
+ /** Number of bytes aready returned (as pages) from the body storage.
+ */
+ private int m_nBodyReturned;
+
+ /** Lacing values. Bit 0 to 7 contain the lacing value (mask
+ 0xFF). Bit 8 is set if the segment belongs to the first
+ packet of the stream (mask 0x100). Bit 9 is set ig the
+ segment belongs to the last packet of the stream (mask 0x200).
+ */
+ private int[] m_anLacingValues;
+
+ /** Granule values.
+ */
+ private long[] m_alGranuleValues;
+
+ /** Number of elements used in m_anLacingValues and m_alGranuleValues.
+ The elements with the index m_nLacingFill is the first free element.
+ */
+ private int m_nLacingFill;
+
+ /** Pointer to the index in m_anLacingValues where the lacing values
+ of the last decoded packet start (??)
+ */
+ private int m_nLacingPacket;
+
+ /**
+ */
+ private int m_nLacingReturned;
+
+ private byte[] m_abHeader;
+
+ private int m_nHeaderFill;
+
+ private boolean m_bBos;
+ private boolean m_bEos;
+ private int m_nPageNo;
+ private long m_lPacketNo;
+ private long m_lGranulePos;
+
+
+
+ public StreamState()
+ {
+ if (TDebug.TraceOggNative) { TDebug.out("StreamState.<init>(): begin"); }
+ if (TDebug.TraceOggNative) { TDebug.out("StreamState.<init>(): end"); }
+ }
+
+
+
+ public void free()
+ {
+ }
+
+
+
+ /** Calls ogg_stream_init().
+ */
+ public int init(int nSerialNo)
+ {
+ m_nSerialNo = nSerialNo;
+ m_abBodyData = new byte[INITIAL_BODY_DATA_SIZE];
+ m_nBodyFill = 0;
+ m_nBodyReturned = 0;
+ m_anLacingValues = new int[INITIAL_LACING_VALUES_SIZE];
+ m_alGranuleValues = new long[INITIAL_LACING_VALUES_SIZE];
+ m_nLacingFill = 0;
+ m_nLacingPacket = 0;
+ m_nLacingReturned = 0;
+
+ m_abHeader = new byte[282];
+ m_nHeaderFill = 0;
+
+ m_bBos = false;
+ m_bEos = false;
+ m_nPageNo = 0;
+ m_lPacketNo = 0;
+ m_lGranulePos = 0;
+
+ // TODO: necessary?
+ for (int i = 0; i < m_abBodyData.length; i++)
+ m_abBodyData[i] = 0;
+ for (int i = 0; i < m_anLacingValues.length; i++)
+ m_anLacingValues[i] = 0;
+ for (int i = 0; i < m_alGranuleValues.length; i++)
+ m_alGranuleValues[i] = 0;
+
+ // TODO: remove return value
+ return 0;
+ }
+
+ /** Calls ogg_stream_clear().
+ */
+ public int clear()
+ {
+ m_nSerialNo = 0;
+ m_abBodyData = null;
+ m_nBodyFill = 0;
+ m_nBodyReturned = 0;
+ m_anLacingValues = null;
+ m_alGranuleValues = null;
+ m_nLacingFill = 0;
+ m_nLacingPacket = 0;
+ m_nLacingReturned = 0;
+
+ m_abHeader = null;
+ m_nHeaderFill = 0;
+
+ m_bBos = false;
+ m_bEos = false;
+ m_nPageNo = 0;
+ m_lPacketNo = 0;
+ m_lGranulePos = 0;
+
+ // TODO: remove return value
+ return 0;
+ }
+
+ /** Calls ogg_stream_reset().
+ */
+ public int reset()
+ {
+ m_nBodyFill = 0;
+ m_nBodyReturned = 0;
+
+ m_nLacingFill = 0;
+ m_nLacingPacket = 0;
+ m_nLacingReturned = 0;
+
+ m_nHeaderFill = 0;
+
+ m_bBos = false;
+ m_bEos = false;
+ m_nPageNo = -1;
+ m_lPacketNo = 0;
+ m_lGranulePos = 0;
+
+ // TODO: remove return value
+ return 0;
+ }
+
+ /** Calls ogg_stream_eos().
+ */
+ public boolean isEOSReached()
+ {
+ return m_bEos;
+ }
+
+ /** Calls ogg_stream_packetin().
+ */
+ /* submit data to the internal buffer of the framing engine */
+ public int packetIn(Packet packet)
+ {
+ int i;
+ byte[] abPacketData = packet.getData();
+ int lacing_vals = abPacketData.length / 255 + 1;
+
+ if (m_nBodyReturned > 0)
+ {
+ /* advance packet data according to the body_returned pointer. We
+ had to keep it around to return a pointer into the buffer last
+ call */
+ m_nBodyFill -= m_nBodyReturned;
+ if (m_nBodyFill > 0)
+ {
+ System.arraycopy(m_abBodyData, m_nBodyReturned,
+ m_abBodyData, 0, m_nBodyFill);
+ }
+ m_nBodyReturned = 0;
+ }
+
+ /* make sure we have the buffer storage */
+ assureBodyDataCapacity(abPacketData.length);
+ assureLacingValuesCapacity(lacing_vals);
+
+ /* Copy in the submitted packet. Yes, the copy is a waste;
+ this is the liability of overly clean abstraction for the
+ time being. It will actually be fairly easy to eliminate
+ the extra copy in the future */
+ System.arraycopy(abPacketData, 0, m_abBodyData, m_nBodyFill,
+ abPacketData.length);
+ m_nBodyFill += abPacketData.length;
+
+ /* Store lacing vals for this packet */
+ for (i = 0; i < lacing_vals - 1; i++)
+ {
+ m_anLacingValues[m_nLacingFill + i] = 255;
+ m_alGranuleValues[m_nLacingFill + i] = m_lGranulePos;
+ }
+ m_anLacingValues[m_nLacingFill + i] = abPacketData.length % 255;
+ m_alGranuleValues[m_nLacingFill + i] = packet.getGranulePos();
+ m_lGranulePos = packet.getGranulePos();
+
+ /* flag the first segment as the beginning of the packet */
+ m_anLacingValues[m_nLacingFill] |= 0x100;
+
+ m_nLacingFill += lacing_vals;
+
+ /* for the sake of completeness */
+ m_lPacketNo++;
+
+ if (packet.isEos())
+ m_bEos = true;
+ return 0;
+ }
+
+
+
+ /** Calls ogg_stream_pageout().
+ */
+/* This constructs pages from buffered packet segments. The pointers
+ returned are to static buffers; do not free. The returned buffers are
+ good only until the next call (using the same ogg_stream_state) */
+ public int pageOut(Page page)
+ {
+ if ((m_bEos && m_nLacingFill > 0) || /* 'were done, now flush' */
+ m_nBodyFill - m_nBodyReturned > 4096 || /* 'page nominal size' */
+ m_nLacingFill >= 255 || /* 'segment table full' */
+ (m_nLacingFill > 0 && ! m_bBos)) /* 'initial header page' */
+ {
+ return flush(page);
+ }
+ /* not enough data to construct a page and not end of stream */
+ return 0;
+ }
+
+
+
+ /** Calls ogg_stream_flush().
+ */
+/* This will flush remaining packets into a page (returning nonzero),
+ even if there is not enough data to trigger a flush normally
+ (undersized page). If there are no packets or partial packets to
+ flush, ogg_stream_flush returns 0. Note that ogg_stream_flush will
+ try to flush a normal sized page like ogg_stream_pageout; a call to
+ ogg_stream_flush does not guarantee that all packets have flushed.
+ Only a return value of 0 from ogg_stream_flush indicates all packet
+ data is flushed into pages.
+
+ since ogg_stream_flush will flush the last page in a stream even if
+ it's undersized, you almost certainly want to use ogg_stream_pageout
+ (and *not* ogg_stream_flush) unless you specifically need to flush
+ an page regardless of size in the middle of a stream.
+*/
+ public int flush(Page page)
+ {
+ int i;
+ int vals = 0;
+ int maxvals = Math.min(m_nLacingFill, 255);
+ int bytes = 0;
+ int acc = 0;
+ long granule_pos = m_alGranuleValues[0];
+
+ if (maxvals == 0)
+ {
+ return 0;
+ }
+
+ /* construct a page */
+ /* decide how many segments to include */
+
+ /* If this is the initial header case, the first page must
+ only include the initial header packet */
+ if (! m_bBos)
+ { /* 'initial header page' case */
+ granule_pos = 0;
+ for (vals = 0; vals < maxvals; vals++)
+ {
+ if ((m_anLacingValues[vals] & 0x0FF) < 255)
+ {
+ vals++;
+ break;
+ }
+ }
+ }
+ else
+ {
+ for (vals = 0; vals < maxvals; vals++)
+ {
+ if (acc > 4096)
+ break;
+ acc += (m_anLacingValues[vals] & 0x0FF);
+ granule_pos = m_alGranuleValues[vals];
+ }
+ }
+
+ /* construct the header in temp storage */
+ m_abHeader[0] = (byte) 'O';
+ m_abHeader[1] = (byte) 'g';
+ m_abHeader[2] = (byte) 'g';
+ m_abHeader[3] = (byte) 'S';
+
+ /* stream structure version */
+ m_abHeader[4] = 0;
+
+ m_abHeader[5] = 0x00;
+ /* continued packet flag? */
+ if ((m_anLacingValues[0] & 0x100) == 0)
+ m_abHeader[5] |= 0x01;
+ /* first page flag? */
+ if (! m_bBos)
+ m_abHeader[5] |= 0x02;
+ /* last page flag? */
+ if (m_bEos && m_nLacingFill == vals)
+ m_abHeader[5] |= 0x04;
+ m_bBos = true;
+
+ /* 64 bits of PCM position */
+ for (i = 6; i < 14; i++)
+ {
+ m_abHeader[i] = (byte) (granule_pos & 0xFF);
+ granule_pos >>>= 8;
+ }
+
+ /* 32 bits of stream serial number */
+ int serialno = m_nSerialNo;
+ for (i = 14; i < 18; i++)
+ {
+ m_abHeader[i] = (byte) (serialno & 0xFF);
+ serialno >>>= 8;
+ }
+
+ /* 32 bits of page counter (we have both counter and page header
+ because this val can roll over) */
+ if (m_nPageNo == -1)
+ {
+ m_nPageNo = 0; /* because someone called
+ stream_reset; this would be a
+ strange thing to do in an
+ encode stream, but it has
+ plausible uses */
+ }
+ int pageno = m_nPageNo++;
+ for (i = 18; i < 22; i++)
+ {
+ m_abHeader[i] = (byte) (pageno & 0xFF);
+ pageno >>>= 8;
+ }
+
+ /* zero for computation; filled in later */
+ m_abHeader[22] = 0;
+ m_abHeader[23] = 0;
+ m_abHeader[24] = 0;
+ m_abHeader[25] = 0;
+
+ /* segment table */
+ m_abHeader[26] = (byte) (vals & 0xFF);
+ for (i = 0; i < vals; i++)
+ {
+ m_abHeader[i + 27] = (byte) (m_anLacingValues[i] & 0xFF);
+ bytes += (m_anLacingValues[i] & 0xFF);
+ }
+
+ /* set pointers in the ogg_page struct */
+ page.setData(m_abHeader, 0, vals + 27,
+ m_abBodyData, m_nBodyReturned, bytes);
+ m_nHeaderFill = vals + 27;
+
+ /* advance the lacing data and set the body_returned pointer */
+
+ m_nLacingFill -= vals;
+ System.arraycopy(m_anLacingValues, vals, m_anLacingValues, 0,
+ m_nLacingFill);
+ System.arraycopy(m_alGranuleValues, vals, m_alGranuleValues, 0,
+ m_nLacingFill);
+ m_nBodyReturned += bytes;
+
+ /* calculate the checksum */
+
+ page.setChecksum();
+
+ /* done */
+ return 1;
+ }
+
+
+
+ /** add the incoming page to the stream state; we decompose the
+ page into packet segments here as well.
+
+ @return 0 on success, -1 if the stream serial number stored in
+ the page does not match the one stored in the stream state or
+ if the protocol version stored in the page is greater than 0.
+ */
+ public int pageIn(Page page)
+ {
+ byte[] header = page.getHeader();
+ byte[] body = page.getBody();
+ int nBodyOffset = 0;
+ int bodysize = body.length;
+ int segptr = 0;
+
+ int version = page.getVersion();
+ boolean continued = page.isContinued();
+ boolean bos = page.isBos();
+ boolean eos = page.isEos();
+ long granulepos = page.getGranulePos();
+ int serialno = page.getSerialNo();
+ int pageno = page.getPageNo();
+ int segments = header[26] & 0xFF;
+
+ /* clean up 'returned data' */
+ int lr = m_nLacingReturned;
+ int br = m_nBodyReturned;
+
+ /* body data */
+ if (br > 0)
+ {
+ m_nBodyFill -= br;
+ if (m_nBodyFill > 0)
+ {
+ System.arraycopy(m_abBodyData, br, m_abBodyData, 0,
+ m_nBodyFill);
+ }
+ m_nBodyReturned = 0;
+ }
+
+ if (lr > 0)
+ {
+ /* segment table */
+ if (m_nLacingFill - lr > 0)
+ {
+ System.arraycopy(m_anLacingValues, lr, m_anLacingValues, 0,
+ m_nLacingFill - lr);
+ System.arraycopy(m_alGranuleValues, lr, m_alGranuleValues, 0,
+ m_nLacingFill - lr);
+ }
+ m_nLacingFill -= lr;
+ m_nLacingPacket -= lr;
+ m_nLacingReturned = 0;
+ }
+
+ /* check the serial number */
+ if (serialno != m_nSerialNo)
+ return -1;
+ if (version > 0)
+ return -1;
+
+ assureLacingValuesCapacity(segments + 1);
+
+ /* are we in sequence? */
+ if (pageno != m_nPageNo)
+ {
+ int i;
+
+ /* unroll previous partial packet (if any) */
+ for (i = m_nLacingPacket; i < m_nLacingFill; i++)
+ m_nBodyFill -= (m_anLacingValues[i] & 0xFF);
+ m_nLacingFill = m_nLacingPacket;
+
+ /* make a note of dropped data in segment table */
+ if (m_nPageNo != -1)
+ {
+ m_anLacingValues[m_nLacingFill] = 0x400;
+ m_nLacingFill++;
+ m_nLacingPacket++;
+ }
+
+ /* are we a 'continued packet' page? If so, we'll need to skip
+ some segments */
+ if (continued)
+ {
+ bos = false;
+ for (; segptr < segments; segptr++)
+ {
+ int val = header[27 + segptr] & 0xFF;
+ nBodyOffset += val;
+ bodysize -= val;
+ if (val < 255)
+ {
+ segptr++;
+ break;
+ }
+ }
+ }
+ }
+
+ if (bodysize > 0)
+ {
+ assureBodyDataCapacity(bodysize);
+ System.arraycopy(body, nBodyOffset, m_abBodyData, m_nBodyFill,
+ bodysize);
+ m_nBodyFill += bodysize;
+ }
+
+ int saved = -1;
+ while (segptr < segments)
+ {
+ int val = header[27 + segptr] & 0xFF;
+ m_anLacingValues[m_nLacingFill] = val;
+ m_alGranuleValues[m_nLacingFill] = -1;
+
+ if (bos)
+ {
+ m_anLacingValues[m_nLacingFill] |= 0x100;
+ bos = false;
+ }
+
+ if (val < 255)
+ saved = m_nLacingFill;
+
+ m_nLacingFill++;
+ segptr++;
+
+ if (val < 255)
+ m_nLacingPacket = m_nLacingFill;
+ }
+
+ /* set the granulepos on the last granuleval of the last full packet */
+ if (saved != -1)
+ {
+ m_alGranuleValues[saved] = granulepos;
+ }
+
+ if (eos)
+ {
+ m_bEos = true;
+ if (m_nLacingFill > 0)
+ m_anLacingValues[m_nLacingFill - 1] |= 0x200;
+ }
+
+ m_nPageNo = pageno + 1;
+
+ return 0;
+ }
+
+
+ /** Calls ogg_stream_packetout().
+ */
+ public int packetOut(Packet packet)
+ {
+ return packetOutInternal(packet, true);
+ }
+
+
+ /** Calls ogg_stream_packetpeek().
+ */
+ public int packetPeek(Packet packet)
+ {
+ return packetOutInternal(packet, false);
+ }
+
+
+ /** Retrieves a packet from the internal storage for emission.
+ This method is called by packetOut and packetPeek.
+
+ @param packet the Packet object to store the retrieved packet
+ data in. May be null if bAdvance is false.
+
+ @param bAdvance should the internal pointers to the packet
+ data storage be advanced to the next packet after retrieving
+ this one? Called with a value of true for ordinary packet out
+ and with a value of false for packet peek.
+
+ @return
+ */
+ private int packetOutInternal(Packet packet, boolean bAdvance)
+ {
+ /* 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 = m_nLacingReturned;
+
+ if (m_nLacingPacket <= ptr)
+ return 0;
+
+ if ((m_anLacingValues[ptr] & 0x400) != 0)
+ {
+ /* we need to tell the codec there's a gap; it might need
+ to handle previous packet dependencies. */
+ m_nLacingReturned++;
+ m_lPacketNo++;
+ return -1;
+ }
+
+ if (packet == null && ! bAdvance)
+ return 1; /* just using peek as an inexpensive way
+ to ask if there's a whole packet
+ waiting */
+
+ /* Gather the whole packet. We'll have no holes or a partial
+ * packet */
+ int size = m_anLacingValues[ptr] & 0xFF;
+ int bytes = size;
+ /* last packet of the stream? */
+ boolean eos = (m_anLacingValues[ptr] & 0x200) != 0;
+ /* first packet of the stream? */
+ boolean bos = (m_anLacingValues[ptr] & 0x100) != 0;
+
+ while (size == 255)
+ {
+ int val = m_anLacingValues[++ptr];
+ size = val & 0xff;
+ if ((val & 0x200) != 0)
+ eos = true;
+ bytes += size;
+ }
+
+ if (packet != null)
+ {
+ packet.setData(m_abBodyData, m_nBodyReturned, bytes);
+ packet.setFlags(bos, eos, m_alGranuleValues[ptr], m_lPacketNo);
+ }
+
+ if (bAdvance)
+ {
+ m_nBodyReturned += bytes;
+ m_nLacingReturned = ptr + 1;
+ m_lPacketNo++;
+ }
+ return 1;
+ }
+
+
+ private void assureBodyDataCapacity(int needed)
+ {
+ if (m_abBodyData.length <= m_nBodyFill + needed)
+ {
+ int nNewSize = m_abBodyData.length + needed + 1024;
+ byte[] abNewBodyData = new byte[nNewSize];
+ System.arraycopy(m_abBodyData, 0, abNewBodyData, 0,
+ m_abBodyData.length);
+ m_abBodyData = abNewBodyData;
+ }
+ }
+
+
+
+ private void assureLacingValuesCapacity(int needed)
+ {
+ if (m_anLacingValues.length <= m_nLacingFill + needed)
+ {
+ int nNewSize = m_anLacingValues.length + needed + 32;
+ int[] anNewLacingValues = new int[nNewSize];
+ System.arraycopy(m_anLacingValues, 0, anNewLacingValues, 0,
+ m_anLacingValues.length);
+ m_anLacingValues = anNewLacingValues;
+ long[] alNewGranuleValues = new long[nNewSize];
+ System.arraycopy(m_alGranuleValues, 0, alNewGranuleValues, 0,
+ m_alGranuleValues.length);
+ m_alGranuleValues = alNewGranuleValues;
+ }
+ }
+}
+
+
+
+
+
+/*** StreamState.java ***/