/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* 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.jogg;
// 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.
public class SyncState{
public byte[] data;
int storage;
int fill;
int returned;
int unsynced;
int headerbytes;
int bodybytes;
public int clear(){
data=null;
return (0);
}
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);
}
// 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 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;
}
}