rockbox/songdbj/org/tritonus/lowlevel/pogg/SyncState.java

340 lines
8.4 KiB
Java
Raw Normal View History

/*
* SyncState.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_sync_state.
*/
public class SyncState
{
/** The stream buffer.
This points to a buffer that holds the incoming data from a stream
until the data is emitted in form of a page.
*/
private byte[] m_abData;
/** The number of bytes in the stream buffer that are used. This
always counts from the beginning of the buffer. So
m_abData[m_nFill] is the first free byte in the buffer.
*/
private int m_nFill;
/** Number of bytes that have been used to construct a returned
page. This is counted from the beginning of the
buffer. m_abData[m_nReturned] is the first valid byte in the
buffer.
*/
private int m_nReturned;
/**
*/
private boolean m_bUnsynced;
/**
*/
private int m_nHeaderBytes;
/**
*/
private int m_nBodyBytes;
/** Page object for re-calculating the checksum.
*/
private Page m_tmpPage;
/** Storage for the checksum saved from the page.
*/
private byte[] m_abChecksum;
public SyncState()
{
if (TDebug.TraceOggNative) { TDebug.out("SyncState.<init>(): begin"); }
m_tmpPage = new Page();
m_abChecksum = new byte[4];
if (TDebug.TraceOggNative) { TDebug.out("SyncState.<init>(): end"); }
}
// TODO: remove calls to this method
public void free()
{
}
/** Calls ogg_sync_init().
*/
public void init()
{
m_abData = null;
m_nFill = 0;
m_nReturned = 0;
m_bUnsynced = false;
m_nHeaderBytes = 0;
m_nBodyBytes = 0;
}
/** Calls ogg_sync_clear().
*/
public void clear()
{
init();
}
/** Calls ogg_sync_reset().
*/
public void reset()
{
m_nFill = 0;
m_nReturned = 0;
m_bUnsynced = false;
m_nHeaderBytes = 0;
m_nBodyBytes = 0;
}
/** Writes to the stream buffer.
*/
public int write(byte[] abBuffer, int nBytes)
{
/* Clear out space that has been previously returned. */
if (m_nReturned > 0)
{
m_nFill -= m_nReturned;
if (m_nFill > 0)
{
System.arraycopy(m_abData, m_nReturned,
m_abData, 0,
m_nFill);
}
m_nReturned = 0;
}
/* Check for enough space in the stream buffer and if it is
* allocated at all. If there isn't sufficient space, extend
* the buffer. */
if (m_abData == null || nBytes > m_abData.length - m_nFill)
{
int nNewSize = nBytes + m_nFill + 4096;
byte[] abOldBuffer = m_abData;
m_abData = new byte[nNewSize];
if (abOldBuffer != null)
{
System.arraycopy(abOldBuffer, 0, m_abData, 0, m_nFill);
}
}
/* Now finally fill with the new data. */
System.arraycopy(abBuffer, 0, m_abData, m_nFill, nBytes);
m_nFill += nBytes;
return 0;
}
/** Synchronizes the stream. This method looks if there is a
complete, valid page in the stream buffer. If a page is found,
it is returned in the passed Page object.
@param page The Page to store the result of the page search
in. The content is only changed if the return value is > 0.
@return if a page has been found at the current location, the
length of the page in bytes is returned. If not enough data
for a page is available in the stream buffer, 0 is
returned. If data in the stream buffer has been skipped
because there is no page at the current position, the skip
amount in bytes is returned as a negative number.
*/
public int pageseek(Page page)
{
int nPage = m_nReturned;
int nBytes = m_nFill - m_nReturned;
if (m_nHeaderBytes == 0)
{
if (nBytes < 27)
{
/* Not enough data for a header. */
return 0;
}
/* Verify capture pattern. */
if (m_abData[nPage] != (byte) 'O' ||
m_abData[nPage + 1] != (byte) 'g' ||
m_abData[nPage + 2] != (byte) 'g' ||
m_abData[nPage + 3] != (byte) 'S')
{
TDebug.out("wrong capture pattern");
return syncFailure();
}
int nHeaderBytes = (m_abData[nPage + 26] & 0xFF) + 27;
if (nBytes < nHeaderBytes)
{
/* Not enough data for header + segment table. */
return 0;
}
/* Count up body length in the segment table. */
for (int i = 0; i < (m_abData[nPage + 26] & 0xFF); i++)
{
m_nBodyBytes += (m_abData[nPage + 27 + i] & 0xFF);
}
m_nHeaderBytes = nHeaderBytes;
}
if (m_nBodyBytes + m_nHeaderBytes > nBytes)
{
/* Not enough data for the whole packet. */
return 0;
}
/* Save the original checksum, set it to zero and recalculate it. */
System.arraycopy(m_abData, nPage + 22, m_abChecksum, 0, 4);
m_abData[nPage + 22] = 0;
m_abData[nPage + 23] = 0;
m_abData[nPage + 24] = 0;
m_abData[nPage + 25] = 0;
m_tmpPage.setData(m_abData, nPage, m_nHeaderBytes,
m_abData, nPage + m_nHeaderBytes, m_nBodyBytes);
// TDebug.out("temporary page:");
// byte[] abHeader = m_tmpPage.getHeader();
// TDebug.out("H0:" + m_abData[nPage + 0] + " - " + abHeader[0]);
// TDebug.out("H1:" + m_abData[nPage + 1] + " - " + abHeader[1]);
// TDebug.out("H2:" + m_abData[nPage + 2] + " - " + abHeader[2]);
// TDebug.out("H3:" + m_abData[nPage + 3] + " - " + abHeader[3]);
// TDebug.out("" + m_abChecksum[0] + " - " + abHeader[22]);
// TDebug.out("" + m_abChecksum[1] + " - " + abHeader[23]);
// TDebug.out("" + m_abChecksum[2] + " - " + abHeader[24]);
// TDebug.out("" + m_abChecksum[3] + " - " + abHeader[25]);
m_tmpPage.setChecksum();
byte[] abHeader = m_tmpPage.getHeader();
//m_tmpPage.free();
if (abHeader[22] != m_abChecksum[0] ||
abHeader[23] != m_abChecksum[1] ||
abHeader[24] != m_abChecksum[2] ||
abHeader[25] != m_abChecksum[3])
{
TDebug.out("wrong checksum");
TDebug.out("" + m_abChecksum[0] + " - " + abHeader[22]);
TDebug.out("" + m_abChecksum[1] + " - " + abHeader[23]);
TDebug.out("" + m_abChecksum[2] + " - " + abHeader[24]);
TDebug.out("" + m_abChecksum[3] + " - " + abHeader[25]);
/* Copy back the saved checksum. */
System.arraycopy(m_abChecksum, 0, m_abData, nPage + 22, 4);
return syncFailure();
}
/* Ok, we have a correct page to emit. */
page.setData(m_abData, nPage, m_nHeaderBytes,
m_abData, nPage + m_nHeaderBytes, m_nBodyBytes);
m_bUnsynced = false;
nBytes = m_nHeaderBytes + m_nBodyBytes;
m_nReturned += nBytes;
m_nHeaderBytes = 0;
m_nBodyBytes = 0;
return nBytes;
}
/** Auxiliary method for pageseek().
*/
private int syncFailure()
{
int nPage = m_nReturned;
int nBytes = m_nFill - m_nReturned;
m_nHeaderBytes = 0;
m_nBodyBytes = 0;
int nNext = -1;
for (int i = 0; i < nBytes - 1; i++)
{
if (m_abData[nPage + 1 + i] == (byte) 'O')
{
nNext = nPage + 1 + i;
break;
}
}
if (nNext == -1)
{
nNext = m_nFill;
}
m_nReturned = nNext;
return -(nNext - nPage);
}
/** Returns a page from the stream buffer, if possible. This
method searches the current data in the stream buffer for the
beginning of a page. If there is one, it is returned in the
passed Page object. A synchronization error is signaled by a
return value of -1. However, only the first synchronization
error is reported. Consecutive sync errors are suppressed.
@param page The Page to store the result of the page search
in. The content is only changed if the return value is 1.
@return 1 if a page has been found, 0 if there is not enough
data, -1 if a synchronization error occured.
*/
public int pageOut(Page page)
{
while (true)
{
int nReturn = pageseek(page);
if (nReturn > 0)
{
return 1;
}
else if (nReturn == 0)
{
return 0;
}
else // nReturn < 0
{
if (! m_bUnsynced)
{
m_bUnsynced = true;
return -1;
}
}
}
}
}
/*** SyncState.java ***/