001/* JOrbis
002 * Copyright (C) 2000 ymnk, JCraft,Inc.
003 *  
004 * Written by: 2000 ymnk<ymnk@jcaft.com>
005 *   
006 * Many thanks to 
007 *   Monty <monty@xiph.org> and 
008 *   The XIPHOPHORUS Company http://www.xiph.org/ .
009 * JOrbis has been based on their awesome works, Vorbis codec.
010 *   
011 * This program is free software; you can redistribute it and/or
012 * modify it under the terms of the GNU Library General Public License
013 * as published by the Free Software Foundation; either version 2 of
014 * the License, or (at your option) any later version.
015   
016 * This program is distributed in the hope that it will be useful,
017 * but WITHOUT ANY WARRANTY; without even the implied warranty of
018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
019 * GNU Library General Public License for more details.
020 * 
021 * You should have received a copy of the GNU Library General Public
022 * License along with this program; if not, write to the Free Software
023 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
024 */
025
026package com.jcraft.jogg;
027
028// DECODING PRIMITIVES: packet streaming layer
029
030// This has two layers to place more of the multi-serialno and paging
031// control in the application's hands.  First, we expose a data buffer
032// using ogg_decode_buffer().  The app either copies into the
033// buffer, or passes it directly to read(), etc.  We then call
034// ogg_decode_wrote() to tell how many bytes we just added.
035//
036// Pages are returned (pointers into the buffer in ogg_sync_state)
037// by ogg_decode_stream().  The page is then submitted to
038// ogg_decode_page() along with the appropriate
039// ogg_stream_state* (ie, matching serialno).  We then get raw
040// packets out calling ogg_stream_packet() with a
041// ogg_stream_state.  See the 'frame-prog.txt' docs for details and
042// example code.
043
044public class SyncState{
045
046  public byte[] data;
047  int storage;
048  int fill;
049  int returned;
050
051  int unsynced;
052  int headerbytes;
053  int bodybytes;
054
055  public int clear(){
056    data=null;
057    return(0);
058  }
059
060// !!!!!!!!!!!!
061//  byte[] buffer(int size){
062  public int buffer(int size){
063    // first, clear out any space that has been previously returned
064    if(returned!=0){
065      fill-=returned;
066      if(fill>0){
067        System.arraycopy(data, returned, data, 0, fill);
068      }
069      returned=0;
070    }
071
072    if(size>storage-fill){
073      // We need to extend the internal buffer
074      int newsize=size+fill+4096; // an extra page to be nice
075      if(data!=null){
076        byte[] foo=new byte[newsize];
077        System.arraycopy(data, 0, foo, 0, data.length);
078        data=foo;
079      }
080      else{
081        data=new byte[newsize];
082      }
083      storage=newsize;
084    }
085
086    // expose a segment at least as large as requested at the fill mark
087//    return((char *)oy->data+oy->fill);
088//    return(data);
089    return(fill);
090  }
091
092  public int wrote(int bytes){
093    if(fill+bytes>storage)return(-1);
094    fill+=bytes;
095    return(0);
096  }
097
098// sync the stream.  This is meant to be useful for finding page
099// boundaries.
100//
101// return values for this:
102// -n) skipped n bytes
103//  0) page not ready; more data (no bytes skipped)
104//  n) page synced at current location; page length n bytes
105  private Page pageseek=new Page();
106  private  byte[] chksum=new byte[4];
107  public int pageseek(Page og){
108    int page=returned;
109    int next;
110    int bytes=fill-returned;
111  
112    if(headerbytes==0){
113      int _headerbytes,i;
114      if(bytes<27)return(0); // not enough for a header
115    
116    /* verify capture pattern */
117//!!!!!!!!!!!
118      if(data[page]!='O' ||
119         data[page+1]!='g' ||
120         data[page+2]!='g' ||
121         data[page+3]!='S'){
122        headerbytes=0;
123        bodybytes=0;
124  
125        // search for possible capture
126        next=0;
127        for(int ii=0; ii<bytes-1; ii++){
128          if(data[page+1+ii]=='O'){next=page+1+ii; break;}
129        }
130    //next=memchr(page+1,'O',bytes-1);
131        if(next==0) next=fill;
132
133        returned=next;
134        return(-(next-page));
135      }
136      _headerbytes=(data[page+26]&0xff)+27;
137      if(bytes<_headerbytes)return(0); // not enough for header + seg table
138    
139      // count up body length in the segment table
140    
141      for(i=0;i<(data[page+26]&0xff);i++){
142        bodybytes+=(data[page+27+i]&0xff);
143      }
144      headerbytes=_headerbytes;
145    }
146  
147    if(bodybytes+headerbytes>bytes)return(0);
148  
149    // The whole test page is buffered.  Verify the checksum
150    synchronized(chksum){
151      // Grab the checksum bytes, set the header field to zero
152    
153      System.arraycopy(data, page+22, chksum, 0, 4);
154      data[page+22]=0;
155      data[page+23]=0;
156      data[page+24]=0;
157      data[page+25]=0;
158    
159      // set up a temp page struct and recompute the checksum
160      Page log=pageseek;
161      log.header_base=data;
162      log.header=page;
163      log.header_len=headerbytes;
164
165      log.body_base=data;
166      log.body=page+headerbytes;
167      log.body_len=bodybytes;
168      log.checksum();
169
170      // Compare
171      if(chksum[0]!=data[page+22] ||
172         chksum[1]!=data[page+23] ||
173         chksum[2]!=data[page+24] ||
174         chksum[3]!=data[page+25]){
175        // D'oh.  Mismatch! Corrupt page (or miscapture and not a page at all)
176        // replace the computed checksum with the one actually read in
177        System.arraycopy(chksum, 0, data, page+22, 4);
178        // Bad checksum. Lose sync */
179
180        headerbytes=0;
181        bodybytes=0;
182        // search for possible capture
183        next=0;
184        for(int ii=0; ii<bytes-1; ii++){
185          if(data[page+1+ii]=='O'){next=page+1+ii; break;}
186        }
187        //next=memchr(page+1,'O',bytes-1);
188        if(next==0) next=fill;
189        returned=next;
190        return(-(next-page));
191      }
192    }
193  
194    // yes, have a whole page all ready to go
195    {
196      page=returned;
197
198      if(og!=null){
199        og.header_base=data;
200        og.header=page;
201        og.header_len=headerbytes;
202        og.body_base=data;
203        og.body=page+headerbytes;
204        og.body_len=bodybytes;
205      }
206
207      unsynced=0;
208      returned+=(bytes=headerbytes+bodybytes);
209      headerbytes=0;
210      bodybytes=0;
211      return(bytes);
212    }
213//  headerbytes=0;
214//  bodybytes=0;
215//  next=0;
216//  for(int ii=0; ii<bytes-1; ii++){
217//    if(data[page+1+ii]=='O'){next=page+1+ii;}
218//  }
219//  //next=memchr(page+1,'O',bytes-1);
220//  if(next==0) next=fill;
221//  returned=next;
222//  return(-(next-page));
223  }
224
225
226// sync the stream and get a page.  Keep trying until we find a page.
227// Supress 'sync errors' after reporting the first.
228//
229// return values:
230//  -1) recapture (hole in data)
231//   0) need more data
232//   1) page returned
233//
234// Returns pointers into buffered data; invalidated by next call to
235// _stream, _clear, _init, or _buffer
236
237  public int pageout(Page og){
238    // all we need to do is verify a page at the head of the stream
239    // buffer.  If it doesn't verify, we look for the next potential
240    // frame
241
242    while(true){
243      int ret=pageseek(og);
244      if(ret>0){
245        // have a page
246        return(1);
247      }
248      if(ret==0){
249        // need more data
250        return(0);
251      }
252    
253      // head did not start a synced page... skipped some bytes
254      if(unsynced==0){
255        unsynced=1;
256        return(-1);
257      }
258      // loop. keep looking
259    }
260  }
261
262// clear things to an initial state.  Good to call, eg, before seeking
263  public int reset(){
264    fill=0;
265    returned=0;
266    unsynced=0;
267    headerbytes=0;
268    bodybytes=0;
269    return(0);
270  }
271  public void init(){}
272}