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}