Beispiel #1
0
        // 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
            }
        }
Beispiel #2
0
        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
            lock(chksum) {
                // Grab the checksum bytes, set the header field to zero

                Array.Copy(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_p;
                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
                    Array.Copy(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);
            }
            //  headerbytes=0;
            //  bodybytes=0;
            //  next=0;
            //  for(int ii=0; ii<bytes-1; ii++){
            //    if(data[page+1+ii]=='O'){next=page+1+ii;}
            //  }
            //  //next=memchr(page+1,'O',bytes-1);
            //  if(next==0) next=fill;
            //  returned=next;
            //  return(-(next-page));
        }
Beispiel #3
0
 private int get_next_page(Page page, long boundary)
 {
     if(boundary>0) { boundary+=offset; }
     while(true) {
         int more;
         if(boundary>0 && offset>=boundary) { return OV_FALSE; }
         more=oy.pageseek(page);
         if(more<0) {offset-=more;}
         else {
             if(more==0) {
                 if(boundary==0) { return OV_FALSE; }
                 //	  if(get_data()<=0)return -1;
                 int ret=get_data();
                 if(ret==0) { return OV_EOF; }
                 if(ret<0) { return OV_EREAD; }
             }
             else {
                 int ret=(int)offset; //!!!
                 offset+=more;
                 return ret;
             }
         }
     }
 }
Beispiel #4
0
 private int get_prev_page(Page page)
 {
     long begin=offset; //!!!
     int ret;
     int offst=-1;
     while(offst==-1) {
         begin-=CHUNKSIZE;
         if(begin<0) {
             begin=0;
         }
         seek_helper(begin);
         while(offset<begin+CHUNKSIZE) {
             ret=get_next_page(page, begin+CHUNKSIZE-offset);
             if(ret==OV_EREAD) { return OV_EREAD; }
             if(ret<0) { break; }
             else { offst=ret; }
         }
     }
     seek_helper(offst); //!!!
     ret=get_next_page(page, CHUNKSIZE);
     if(ret<0) {
         //System.err.println("Missed page fencepost at end of logical bitstream Exiting");
         //System.exit(1);
         return OV_EFAULT;
     }
     return offst;
 }
Beispiel #5
0
        int bisect_forward_serialno(long begin, long searched, long end, int currentno, int m)
        {
            long endsearched=end;
            long next=end;
            Page page=new Page();
            int ret;

            while(searched<endsearched) {
                long bisect;
                if(endsearched-searched<CHUNKSIZE) {
                    bisect=searched;
                }
                else {
                    bisect=(searched+endsearched)/2;
                }

                seek_helper(bisect);
                ret=get_next_page(page, -1);
                if(ret==OV_EREAD) { return OV_EREAD; }
                if(ret<0 || page.serialno()!=currentno) {
                    endsearched=bisect;
                    if(ret>=0) { next=ret; }
                }
                else {
                    searched=ret+page.header_len+page.body_len;
                }
            }
            seek_helper(next);
            ret=get_next_page(page, -1);
            if(ret==OV_EREAD) { return OV_EREAD; }

            if(searched>=end || ret==-1) {
                links=m+1;
                offsets=new long[m+2];
                offsets[m+1]=searched;
            }
            else {
                ret=bisect_forward_serialno(next, offset, end, page.serialno(), m+1);
                if(ret==OV_EREAD) { return OV_EREAD; }
            }
            offsets[m]=begin;
            return 0;
        }
Beispiel #6
0
        // uses the local ogg_stream storage in vf; this is important for
        // non-streaming input sources
        int fetch_headers(Info vi, Comment vc, int[] serialno, Page og_ptr)
        {
            //System.err.println("fetch_headers");
            Page og=new Page();
            Packet op=new Packet();
            int ret;

            if(og_ptr==null) {
                ret=get_next_page(og, CHUNKSIZE);
                if(ret==OV_EREAD) { return OV_EREAD; }
                if(ret<0) { return OV_ENOTVORBIS; }
                og_ptr=og;
            }

            if(serialno!=null) { serialno[0]=og_ptr.serialno(); }

            os.init(og_ptr.serialno());

            // extract the initial header from the first page and verify that the
            // Ogg bitstream is in fact Vorbis data

            vi.init();
            vc.init();

            int i=0;
            while(i<3) {
                os.pagein(og_ptr);
                while(i<3) {
                    int result=os.packetout(op);
                    if(result==0) { break; }
                    if(result==-1) {
                        Console.Error.WriteLine("Corrupt header in logical bitstream.");
                        //goto bail_header;
                        vi.clear();
                        vc.clear();
                        os.clear();
                        return -1;
                    }
                    if(vi.synthesis_headerin(vc, op)!=0) {
                        Console.Error.WriteLine("Illegal header in logical bitstream.");
                        //goto bail_header;
                        vi.clear();
                        vc.clear();
                        os.clear();
                        return -1;
                    }
                    i++;
                }
                if(i<3)
                    if(get_next_page(og_ptr, 1)<0) {
                        Console.Error.WriteLine("Missing header in logical bitstream.");
                        //goto bail_header;
                        vi.clear();
                        vc.clear();
                        os.clear();
                        return -1;
                    }
            }
            return 0;
        }
Beispiel #7
0
        // fetch and process a packet.  Handles the case where we're at a
        // bitstream boundary and dumps the decoding machine.  If the decoding
        // machine is unloaded, it loads it.  It also keeps pcm_offset up to
        // date (seek and read both use this.  seek uses a special hack with
        // readp).
        //
        // return: -1) hole in the data (lost packet)
        //          0) need more date (only if readp==0)/eof
        //          1) got a packet
        int process_packet(int readp)
        {
            Page og=new Page();

            // handle one packet.  Try to fetch it from current stream state
            // extract packets from page
            while(true) {
                // process a packet if we can.  If the machine isn't loaded,
                // neither is a page
                if(decode_ready) {
                    Packet op=new Packet();
                    int result=os.packetout(op);
                    long granulepos;
                    // if(result==-1)return(-1); // hole in the data. For now, swallow
                    // and go. We'll need to add a real
                    // error code in a bit.
                    if(result>0) {
                        // got a packet.  process it
                        granulepos=op.granulepos;
                        if(vb.synthesis(op)==0) {
                            // lazy check for lazy
                            // header handling.  The
                            // header packets aren't
                            // audio, so if/when we
                            // submit them,
                            // vorbis_synthesis will
                            // reject them
                            // suck in the synthesis data and track bitrate
                            {
                                int oldsamples=vd.synthesis_pcmout(null, null);
                                vd.synthesis_blockin(vb);
                                samptrack+=vd.synthesis_pcmout(null, null)-oldsamples;
                                bittrack+=op.bytes*8;
                            }

                            // update the pcm offset.
                            if(granulepos!=-1 && op.e_o_s==0) {
                                int link=(skable?current_link:0);
                                int samples;
                                // this packet has a pcm_offset on it (the last packet
                                // completed on a page carries the offset) After processing
                                // (above), we know the pcm position of the *last* sample
                                // ready to be returned. Find the offset of the *first*
                                //
                                // As an aside, this trick is inaccurate if we begin
                                // reading anew right at the last page; the end-of-stream
                                // granulepos declares the last frame in the stream, and the
                                // last packet of the last page may be a partial frame.
                                // So, we need a previous granulepos from an in-sequence page
                                // to have a reference point.  Thus the !op.e_o_s clause above

                                samples=vd.synthesis_pcmout(null, null);
                                granulepos-=samples;
                                for(int i=0; i<link; i++) {
                                    granulepos+=pcmlengths[i];
                                }
                                pcm_offset=granulepos;
                            }
                            return(1);
                        }
                    }
                }

                if(readp==0) { return(0); }
                if(get_next_page(og,-1)<0) { return(0); } // eof. leave unitialized

                // bitrate tracking; add the header's bytes here, the body bytes
                // are done by packet above
                bittrack+=og.header_len*8;

                // has our decoding just traversed a bitstream boundary?
                if(decode_ready) {
                    if(current_serialno!=og.serialno()) {
                        decode_clear();
                    }
                }

                // Do we need to load a new machine before submitting the page?
                // This is different in the seekable and non-seekable cases.
                //
                // In the seekable case, we already have all the header
                // information loaded and cached; we just initialize the machine
                // with it and continue on our merry way.
                //
                // In the non-seekable (streaming) case, we'll only be at a
                // boundary if we just left the previous logical bitstream and
                // we're now nominally at the header of the next bitstream

                if(!decode_ready) {
                    int i;
                    if(skable) {
                        current_serialno=og.serialno();

                        // match the serialno to bitstream section.  We use this rather than
                        // offset positions to avoid problems near logical bitstream
                        // boundaries
                        for(i=0; i<links; i++) {
                            if(serialnos[i]==current_serialno) { break; }
                        }
                        if(i==links) { return(-1); } // sign of a bogus stream.  error out,
                        // leave machine uninitialized
                        current_link=i;

                        os.init(current_serialno);
                        os.reset();

                    }
                    else {
                        // we're streaming
                        // fetch the three header packets, build the info struct
                        int[] foo = new int[1];
                        int ret=fetch_headers(vi[0], vc[0], foo, og);
                        current_serialno=foo[0];
                        if(ret!=0) { return ret; }
                        current_link++;
                        i=0;
                    }
                    make_decode_ready();
                }
                os.pagein(og);
            }
        }
Beispiel #8
0
        // seek to a sample offset relative to the decompressed pcm stream
        // returns zero on success, nonzero on failure
        public int pcm_seek(long pos)
        {
            int link=-1;
            long total=pcm_total(-1);

            if(!skable) { return(-1); } // don't dump machine if we can't seek
            if(pos<0 || pos>total) {
                //goto seek_error;
                pcm_offset=-1;
                decode_clear();
                return -1;
            }

            // which bitstream section does this pcm offset occur in?
            for(link=links-1; link>=0; link--) {
                total-=pcmlengths[link];
                if(pos>=total) { break; }
            }

            // search within the logical bitstream for the page with the highest
            // pcm_pos preceeding (or equal to) pos.  There is a danger here;
            // missing pages or incorrect frame number information in the
            // bitstream could make our task impossible.  Account for that (it
            // would be an error condition)
            {
                long target=pos-total;
                long end=offsets[link+1];
                long begin=offsets[link];
                int best=(int)begin;

                Page og=new Page();
                while(begin<end) {
                    long bisect;
                    int ret;

                    if(end-begin<CHUNKSIZE) {
                        bisect=begin;
                    }
                    else {
                        bisect=(end+begin)/2;
                    }

                    seek_helper(bisect);
                    ret=get_next_page(og,end-bisect);

                    if(ret==-1) {
                        end=bisect;
                    }
                    else {
                        long granulepos=og.granulepos();
                        if(granulepos<target) {
                            best=ret;  // raw offset of packet with granulepos
                            begin=offset; // raw offset of next packet
                        }
                        else {
                            end=bisect;
                        }
                    }
                }
                // found our page. seek to it (call raw_seek).
                if(raw_seek(best)!=0) {
                    //goto seek_error;
                    pcm_offset=-1;
                    decode_clear();
                    return -1;
                }
            }

            // verify result
            if(pcm_offset>=pos) {
                //goto seek_error;
                pcm_offset=-1;
                decode_clear();
                return -1;
            }
            if(pos>pcm_total(-1)) {
                //goto seek_error;
                pcm_offset=-1;
                decode_clear();
                return -1;
            }

            // discard samples until we reach the desired position. Crossing a
            // logical bitstream boundary with abandon is OK.
            while(pcm_offset<pos) {
                float[][] pcm;
                int target=(int)(pos-pcm_offset);
                float[][][] _pcm=new float[1][][];
                int[] _index=new int[getInfo(-1).channels];
                int samples=vd.synthesis_pcmout(_pcm, _index);
                pcm=_pcm[0];

                if(samples>target) { samples=target; }
                vd.synthesis_read(samples);
                pcm_offset+=samples;

                if(samples<target)
                    if(process_packet(1)==0) {
                        pcm_offset=pcm_total(-1); // eof
                    }
            }
            return 0;

            // seek_error:
            // dump machine so we're in a known state
            //pcm_offset=-1;
            //decode_clear();
            //return -1;
        }
Beispiel #9
0
        // last step of the OggVorbis_File initialization; get all the
        // vorbis_info structs and PCM positions.  Only called by the seekable
        // initialization (local stream storage is hacked slightly; pay
        // attention to how that's done)
        void prefetch_all_headers(Info first_i,Comment first_c, int dataoffset)
        {
            Page og=new Page();
            int ret;

            vi=new Info[links];
            vc=new Comment[links];
            dataoffsets=new long[links];
            pcmlengths=new long[links];
            serialnos=new int[links];

            for(int i=0; i<links; i++) {
                if(first_i!=null && first_c!=null && i==0) {
                    // we already grabbed the initial header earlier.  This just
                    // saves the waste of grabbing it again
                    // !!!!!!!!!!!!!
                    vi[i]=first_i;
                    //memcpy(vf->vi+i,first_i,sizeof(vorbis_info));
                    vc[i]=first_c;
                    //memcpy(vf->vc+i,first_c,sizeof(vorbis_comment));
                    dataoffsets[i]=dataoffset;
                }
                else {
                    // seek to the location of the initial header
                    seek_helper(offsets[i]); //!!!
                    if(fetch_headers(vi[i], vc[i], null, null)==-1) {
                        Console.Error.WriteLine("Error opening logical bitstream #"+(i+1)+"\n");
                        dataoffsets[i]=-1;
                    }
                    else {
                        dataoffsets[i]=offset;
                        os.clear();
                    }
                }

                // get the serial number and PCM length of this link. To do this,
                // get the last page of the stream
                long end=offsets[i+1]; //!!!
                seek_helper(end);

                while(true) {
                    ret=get_prev_page(og);
                    if(ret==-1) {
                        // this should not be possible
                        Console.Error.WriteLine("Could not find last page of logical "+
                                                "bitstream #"+(i)+"\n");
                        vi[i].clear();
                        vc[i].clear();
                        break;
                    }
                    if(og.granulepos()!=-1) {
                        serialnos[i]=og.serialno();
                        pcmlengths[i]=og.granulepos();
                        break;
                    }
                }
            }
        }
Beispiel #10
0
 int open_seekable()
 {
     Info initial_i=new Info();
     Comment initial_c=new Comment();
     int serialno;
     long end;
     int ret;
     int dataoffset;
     Page og=new Page();
     // is this even vorbis...?
     int[] foo=new int[1];
     ret=fetch_headers(initial_i, initial_c, foo, null);
     serialno=foo[0];
     dataoffset=(int)offset; //!!
     os.clear();
     if(ret==-1) { return(-1); }
     // we can seek, so set out learning all about this file
     skable=true;
     //(callbacks.seek_func)(datasource, 0, SEEK_END);
     fseek(datasource, 0, SEEK_END);
     //offset=end=(callbacks.tell_func)(datasource);
     offset=ftell(datasource);
     end=offset;
     // We get the offset for the last page of the physical bitstream.
     // Most OggVorbis files will contain a single logical bitstream
     end=get_prev_page(og);
     // moer than one logical bitstream?
     if(og.serialno()!=serialno) {
         // Chained bitstream. Bisect-search each logical bitstream
         // section.  Do so based on serial number only
         if(bisect_forward_serialno(0,0,end+1,serialno,0)<0) {
             clear();
             return OV_EREAD;
         }
     }
     else {
         // Only one logical bitstream
         if(bisect_forward_serialno(0,end,end+1,serialno,0)<0) {
             clear();
             return OV_EREAD;
         }
     }
     prefetch_all_headers(initial_i, initial_c, dataoffset);
     return(raw_seek(0));
 }
Beispiel #11
0
		/* 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 og)
		{
			if((e_o_s!=0&&lacing_fill!=0) ||  /* 'were done, now flush' case */
				body_fill-body_returned> 4096 ||     /* 'page nominal size' case */
				lacing_fill>=255 ||          /* 'segment table full' case */
				(lacing_fill!=0&&b_o_s==0))
			{  /* 'initial header page' case */
				return flush(og);
			}
			return 0;
		}
Beispiel #12
0
		/* 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 gurantee that all packets have flushed.
			   Only a return value of 0 from ogg_stream_flush indicates all packet
			   data is flushed into pages.

			   ogg_stream_page 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 need to flush an undersized
			   page in the middle of a stream for some reason. */

		public int flush(Page og)
		{

			//System.out.println(this+" ---body_returned: "+body_returned);

			int i;
			int vals=0;
			int maxvals=(lacing_fill>255?255:lacing_fill);
			int bytes=0;
			int acc=0;
			long granule_pos=granule_vals[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(b_o_s==0)
			{  /* 'initial header page' case */
				granule_pos=0;
				for(vals=0;vals<maxvals;vals++)
				{
					if((lacing_vals[vals]&0x0ff)<255)
					{
						vals++;
						break;
					}
				}
			}
			else
			{
				for(vals=0;vals<maxvals;vals++)
				{
					if(acc>4096)break;
					acc+=(lacing_vals[vals]&0x0ff);
					granule_pos=granule_vals[vals];
				}
			}

			/* construct the header in temp storage */

		    String oggs_str = "OggS";
			Encoding AE = Encoding.UTF8;
			byte[] oggs_byt = AE.GetBytes(oggs_str);
			Array.Copy(oggs_byt, 0, header, 0, oggs_byt.Length);
  
			/* stream structure version */
			header[4]=0x00;

			/* continued packet flag? */
			header[5]=0x00;
			if((lacing_vals[0]&0x100)==0)header[5]|=0x01;
			/* first page flag? */
			if(b_o_s==0) header[5]|=0x02;
			/* last page flag? */
			if(e_o_s!=0 && lacing_fill==vals) header[5]|=0x04;
			b_o_s=1;

			/* 64 bits of PCM position */
			for(i=6;i<14;i++)
			{
				header[i]=(byte)granule_pos;
				granule_pos>>=8;
			}

			/* 32 bits of stream serial number */
		{
			int _serialno=serialno;
			for(i=14;i<18;i++)
			{
				header[i]=(byte)_serialno;
				_serialno>>=8;
			}
		}

			/* 32 bits of page counter (we have both counter and page header
				   because this val can roll over) */
			if(pageno==-1)pageno=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=pageno++;
			for(i=18;i<22;i++)
			{
				header[i]=(byte)_pageno;
				_pageno>>=8;
			}
		}
  
			/* zero for computation; filled in later */
			header[22]=0;
			header[23]=0;
			header[24]=0;
			header[25]=0;
  
			/* segment table */
			header[26]=(byte)vals;
			for(i=0;i<vals;i++)
			{
				header[i+27]=(byte)lacing_vals[i];
				bytes+=(header[i+27]&0xff);
			}
  
			/* set pointers in the ogg_page struct */
			og.header_base=header;
			og.header=0;
			og.header_len=header_fill=vals+27;
			og.body_base=body_data;
			og.body=body_returned;
			og.body_len=bytes;

			/* advance the lacing data and set the body_returned pointer */
  
			lacing_fill-=vals;
			Array.Copy(lacing_vals, vals, lacing_vals, 0, lacing_fill*4);
			Array.Copy(granule_vals, vals, granule_vals, 0, lacing_fill*8);
			body_returned+=bytes;
  
			/* calculate the checksum */
  
			og.checksum();

			/* done */
			return(1);
		}
Beispiel #13
0
		// add the incoming page to the stream state; we decompose the page
		// into packet segments here as well.

		public int pagein(Page og)
		{
			byte[] header_base=og.header_base;
			int header=og.header;
			byte[] body_base=og.body_base;
			int body=og.body;
			int bodysize=og.body_len;
			int segptr=0;

			int version=og.version();
			int continued=og.continued();
			int bos=og.bos();
			int eos=og.eos();
			long granulepos=og.granulepos();
			int _serialno=og.serialno();
			int _pageno=og.pageno();
			int segments=header_base[header+26]&0xff;

			// clean up 'returned data'
		{
			int lr=lacing_returned;
			int br=body_returned;

			// body data

			//System.out.println("br="+br+", body_fill="+body_fill);

			if(br!=0)
			{
				body_fill-=br;
				if(body_fill!=0)
				{
					Array.Copy(body_data, br, body_data, 0, body_fill);
				}
				body_returned=0;
			}

			//System.out.println("?? br="+br+", body_fill="+body_fill+" body_returned="+body_returned);

			if(lr!=0)
			{
				// segment table
				if((lacing_fill-lr)!=0)
				{
					Array.Copy(lacing_vals, lr, lacing_vals, 0, lacing_fill-lr);
					Array.Copy(granule_vals, lr, granule_vals, 0, lacing_fill-lr);
				}
				lacing_fill-=lr;
				lacing_packet-=lr;
				lacing_returned=0;
			}
		}

			// check the serial number
			if(_serialno!=serialno)return(-1);
			if(version>0)return(-1);

			lacing_expand(segments+1);

			// are we in sequence?
			if(_pageno!=pageno)
			{
				int i;

				// unroll previous partial packet (if any)
				for(i=lacing_packet;i<lacing_fill;i++)
				{
					body_fill-=lacing_vals[i]&0xff;
					//System.out.println("??");
				}
				lacing_fill=lacing_packet;

				// make a note of dropped data in segment table
				if(pageno!=-1)
				{
					lacing_vals[lacing_fill++]=0x400;
					lacing_packet++;
				}

				// are we a 'continued packet' page?  If so, we'll need to skip
				// some segments
				if(continued!=0)
				{
					bos=0;
					for(;segptr<segments;segptr++)
					{
						int val=(header_base[header+27+segptr]&0xff);
						body+=val;
						bodysize-=val;
						if(val<255)
						{
							segptr++;
							break;
						}
					}
				}
			}

			//System.out.println("bodysize="+bodysize);

			if(bodysize!=0)
			{
				body_expand(bodysize);
				Array.Copy(body_base, body, body_data, body_fill, bodysize);
				body_fill+=bodysize;
			}

			//System.out.println("bodyfill="+body_fill);

		{
			int saved=-1;
			while(segptr<segments)
			{
				int val=(header_base[header+27+segptr]&0xff);
				lacing_vals[lacing_fill]=val;
				granule_vals[lacing_fill]=-1;
      
				if(bos!=0)
				{
					lacing_vals[lacing_fill]|=0x100;
					bos=0;
				}
      
				if(val<255)saved=lacing_fill;
      
				lacing_fill++;
				segptr++;
      
				if(val<255)lacing_packet=lacing_fill;
			}
  
			/* set the granulepos on the last pcmval of the last full packet */
			if(saved!=-1)
			{
				granule_vals[saved]=granulepos;
			}
		}

			if(eos!=0)
			{
				e_o_s=1;
				if(lacing_fill>0)
					lacing_vals[lacing_fill-1]|=0x200;
			}

			pageno=_pageno+1;
			return(0);
		}