private 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); } offset = input.Position = input.Length; 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)); }
// 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); }
// 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; }
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; }
// 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); } }
// 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; } } } }
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)); }
// 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); }
// 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) private void prefetch_all_headers(Info first_i, Comment first_c, int dataoffset) { Page og = new Page(); int ret; vis = new Info[links]; vcs = 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 // !!!!!!!!!!!!! vis[i] = first_i; //memcpy(vf->vi+i,first_i,sizeof(vorbis_info)); vcs[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(vis[i], vcs[i], null, null) == -1) { throw new Exception("Error opening logical bitstream #" + (i + 1) + "\n"); } 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 throw new Exception("Could not find last page of logical " + "bitstream #" + (i) + "\n"); } if (og.granulepos() != -1) { serialnos[i] = og.serialno(); pcmlengths[i] = og.granulepos(); break; } } } }
// uses the local ogg_stream storage in vf; this is important for // non-streaming input sources private 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) { throw new Exception("Corrupt header in logical bitstream."); } if (vi.synthesis_headerin(vc, op) != 0) { throw new Exception("Illegal header in logical bitstream."); } i++; } if (i < 3) { if (get_next_page(og_ptr, 1) < 0) { throw new Exception("Missing header in logical bitstream."); } } } return(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 private 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 = current_link; 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; 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(); make_decode_ready(); } os.pagein(og); } }
private 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); }