private boolean openSeekable() { final Info initialInfo = new Info(); final Comment initialComment = new Comment(); this.m_chunkSize = Math.min(8500, (int) length(this.m_vorbisStream)); final Page page = new Page(); final int[] testSerialno = {0}; final int ret = this.fetchHeaders(initialInfo, initialComment, testSerialno, null); final int serialno = testSerialno[0]; final int dataOffset = (int) this.m_offset; this.m_oggStreamState.clear(); if (ret < 0) { return false; } seek(this.m_vorbisStream, 0L, 1); this.m_offset = tell(this.m_vorbisStream); final long end = this.getPreviousPage(page); if (page.serialno() != serialno) { if (this.bisectForwardSerialno(0L, 0L, end + 1L, serialno, 0) < 0) { return false; } } else if (this.bisectForwardSerialno(0L, end, end + 1L, serialno, 0) < 0) { return false; } this.prefetchAllHeaders(initialInfo, initialComment, dataOffset); this.rawSeek(this.m_dataOffsets[0]); return true; }
private void pageOut() throws IOException { if (oggPage == null) { oggPage = new Page(); } for (; ; ) { switch (oggSyncState.pageout(oggPage)) { case 0: return; case 1: if (oggStreamState == null) { oggStreamState = new StreamState(); oggStreamState.init(oggPage.serialno()); oggStreamState.reset(); } if (oggStreamState.pagein(oggPage) < 0) { throw new IOException("error reading ogg page"); } else { packetOut(); if (oggPage.eos() != 0) { throw new EOFException(); } } break; default: throw new IOException("ogg input format error"); } } }
/** * 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) * * @param firstInfo first info * @param firstComment first comment * @param dataoffset data offset * @throws JOrbisException if an error occurs */ void prefetchAllHeaders(Info firstInfo, Comment firstComment, int dataoffset) throws JOrbisException { 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 (firstInfo != null && firstComment != null && i == 0) { // we already grabbed the initial header earlier. This just // saves the waste of grabbing it again vi[i] = firstInfo; vc[i] = firstComment; dataoffsets[i] = dataoffset; } else { // seek to the location of the initial header seekHelper(offsets[i]); // !!! vi[i] = new Info(); vc[i] = new Comment(); if (fetchHeaders(vi[i], vc[i], null, null) == -1) { 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]; // !!! seekHelper(end); while (true) { ret = getPrevPage(og); if (ret == -1) { // this should not be possible vi[i].clear(); vc[i].clear(); break; } if (og.granulepos() != -1) { serialnos[i] = og.serialno(); pcmlengths[i] = og.granulepos(); break; } } } }
/** @return negative value on error */ private int seekDuration() { this.startOffset = getNextPageOffset(); seek(getData().size()); Page og = new Page(); int endOffset = getPrevPage(og, serialno); if (endOffset < 0) { return -1; } else { numFrames = (int) og.granulepos(); seek(startOffset); return 0; } }
/** * Bisect forward serial number. * * @param begin beginning * @param searched searched * @param end end * @param currentno current number * @param m member * @return success flag OV_* */ int bisectForwardSerialno(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; } seekHelper(bisect); ret = getNextPage(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.headerLen + page.bodyLen; } } seekHelper(next); ret = getNextPage(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 = bisectForwardSerialno(next, offset, end, page.serialno(), m + 1); if (ret == OV_EREAD) { return OV_EREAD; } } offsets[m] = begin; return 0; }
public int fetchHeaders(final Info info, final Comment comment, final int[] serialno, Page page) { if (page == null) { page = new Page(); final int retValue = this.getNextPage(page, this.m_chunkSize); if (retValue == -128) { return -128; } if (retValue < 0) { return -130; } } if (serialno != null) { serialno[0] = page.serialno(); } this.m_oggStreamState.init(page.serialno()); info.init(); comment.init(); final Packet packet = new Packet(); int i = 0; while (i < 3) { this.m_oggStreamState.pagein(page); while (i < 3) { final int result = this.m_oggStreamState.packetout(packet); if (result == 0) { break; } if (result == -1) { info.clear(); this.m_oggStreamState.clear(); return -1; } if (info.synthesis_headerin(comment, packet) != 0) { info.clear(); this.m_oggStreamState.clear(); return -1; } ++i; } if (i < 3 && this.getNextPage(page, 1L) < 0) { info.clear(); this.m_oggStreamState.clear(); return -1; } } return 0; }
/** * Open seekable. * * @return success value * @throws JOrbisException if an error occurs */ int openSeekable() throws JOrbisException { Info initialInfo = new Info(); Comment initialComment = new Comment(); int serialno; long end; int ret; int dataoffset; Page og = new Page(); // is this even vorbis...? int[] foo = new int[1]; ret = fetchHeaders(initialInfo, initialComment, foo, null); serialno = foo[0]; dataoffset = (int) offset; // !! os.clear(); if (ret == -1) { return (-1); } if (ret < 0) { return (ret); } // we can seek, so set out learning all about this file seekable = true; fseek(datasource, 0, SEEK_END); 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 = getPrevPage(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 (bisectForwardSerialno(0, 0, end + 1, serialno, 0) < 0) { clear(); return OV_EREAD; } } else { // Only one logical bitstream if (bisectForwardSerialno(0, end, end + 1, serialno, 0) < 0) { clear(); return OV_EREAD; } } prefetchAllHeaders(initialInfo, initialComment, dataoffset); return 0; }
private int bisectForwardSerialno( final long begin, long searched, final long end, final int currentno, final int m) { long endsearched = end; long next = end; final Page page = new Page(); while (searched < endsearched) { long bisect; if (endsearched - searched < this.m_chunkSize) { bisect = searched; } else { bisect = (searched + endsearched) / 2L; } this.seekHelper(bisect); final int ret = this.getNextPage(page, -1L); if (ret == -128) { return -128; } if (ret < 0 || page.serialno() != currentno) { endsearched = bisect; if (ret < 0) { continue; } next = ret; } else { searched = ret + page.header_len + page.body_len; } } this.seekHelper(next); int ret = this.getNextPage(page, -1L); if (ret == -128) { return -128; } if (searched >= end || ret == -1) { this.m_links = m + 1; (this.m_offsets = new long[m + 2])[m + 1] = searched; } else { ret = this.bisectForwardSerialno(next, this.m_offset, end, page.serialno(), m + 1); if (ret == -128) { return -128; } } this.m_offsets[m] = begin; return 0; }
private void prefetchAllHeaders( final Info firstInfo, final Comment firstComment, final int dataoffset) { final Page page = new Page(); this.m_info = new Info[this.m_links]; this.m_comments = new Comment[this.m_links]; this.m_dataOffsets = new long[this.m_links]; this.m_pcmLengths = new long[this.m_links]; this.m_serialnos = new int[this.m_links]; for (int i = 0; i < this.m_links; ++i) { if (firstInfo != null && firstComment != null && i == 0) { this.m_info[i] = firstInfo; this.m_comments[i] = firstComment; this.m_dataOffsets[i] = dataoffset; } else { this.seekHelper(this.m_offsets[i]); this.m_info[i] = new Info(); this.m_comments[i] = new Comment(); if (this.fetchHeaders(this.m_info[i], this.m_comments[i], null, null) == -1) { this.m_dataOffsets[i] = -1L; } else { this.m_dataOffsets[i] = this.m_offset; this.m_oggStreamState.clear(); } } final long end = this.m_offsets[i + 1]; this.seekHelper(end); while (true) { final int ret = this.getPreviousPage(page); if (ret == -1) { this.m_info[i].clear(); break; } if (page.granulepos() != -1L) { this.m_serialnos[i] = page.serialno(); this.m_pcmLengths[i] = page.granulepos(); break; } } } }
@Override public int pcmSeek(final long pos) { if (!this.m_vorbisStream.isSeekable()) { return -1; } long total = this.pcmTotal(-1); if (pos < 0L || pos > total) { this.m_pcmOffset = -1L; this.decodeClear(); return -1; } int link; for (link = this.m_links - 1; link >= 0; --link) { total -= this.m_pcmLengths[link]; if (pos >= total) { break; } } final long target = pos - total; long end = this.m_offsets[link + 1]; long begin = this.m_offsets[link]; int best = (int) begin; final Page page = new Page(); while (begin < end) { long bisect; if (end - begin < this.m_chunkSize) { bisect = begin; } else { bisect = (end + begin) / 2L; } this.seekHelper(bisect); final int ret = this.getNextPage(page, end - bisect); if (ret == -1) { end = bisect; } else { final long granulepos = page.granulepos(); if (granulepos < target) { best = ret; begin = this.m_offset; } else { end = bisect; } } } if (this.rawSeek(best) != 0) { this.m_pcmOffset = -1L; this.decodeClear(); return -1; } if (this.m_pcmOffset >= pos) { this.m_pcmOffset = -1L; this.decodeClear(); return -1; } if (pos > this.pcmTotal(-1)) { this.m_pcmOffset = -1L; this.decodeClear(); return -1; } while (this.m_pcmOffset < pos) { final int target2 = (int) (pos - this.m_pcmOffset); final int[] _index = new int[this.m_info[this.m_currentLink].channelsCount]; int samples = this.m_dspState.synthesis_pcmout(this.m_pcmf_buffer, _index); if (samples > target2) { samples = target2; } this.m_dspState.synthesis_read(samples); this.m_pcmOffset += samples; if (samples < target2 && this.processPacket(true) == 0) { this.m_pcmOffset = this.pcmTotal(-1); } } return 0; }
/** * @return a negative number if not enough data available, 0 for failure or a positive number for * success. */ private int getHeaders(Info tempInfo, Comment tempComment) { Page og = new Page(); Packet op = new Packet(); boolean done = false; int packets = 0; // Parse the headers // Only interested in Vorbis stream while (!done) { int ret = getDataChunk(); if (ret <= 0) { return ret; } while (oy.pageout(og) > 0) { StreamState test = new StreamState(); // is this a mandated initial header? If not, stop parsing if (og.bos() == 0) { if (os != null) { os.pagein(og); } done = true; break; } int testSerialNo = og.serialno(); test.init(testSerialNo); test.pagein(og); test.packetout(op); if (packets == 0 && tempInfo.synthesis_headerin(tempComment, op) >= 0) { os = test; serialno = testSerialNo; packets = 1; } else { // Ignore unknown stream test.clear(); } } } if (packets == 0) { return 0; } // we've now identified all the bitstreams. parse the secondary header packets. while (packets < 3) { int ret; // look for more vorbis header packets while (packets < 3 && ((ret = os.packetout(op)) != 0)) { if (ret < 0 || tempInfo.synthesis_headerin(tempComment, op) != 0) { return 0; } packets++; } // The header pages/packets will arrive before anything else we // care about, or the stream is not obeying spec if (oy.pageout(og) > 0) { os.pagein(og); } else { ret = getDataChunk(); if (ret <= 0) { return ret; } } } vd.synthesis_init(tempInfo); return 1; }
/* * Taken from the JOrbis Player */ private void playStream(Thread me) throws InternalException { boolean chained = false; initJOrbis(); while (true) { if (checkState()) { return; } int eos = 0; int index = oy.buffer(BUFSIZE); buffer = oy.data; try { bytes = bitStream.read(buffer, index, BUFSIZE); } catch (Exception e) { throw new InternalException(e); } oy.wrote(bytes); if (chained) { chained = false; } else { if (oy.pageout(og) != 1) { if (bytes < BUFSIZE) break; throw new InternalException("Input does not appear to be an Ogg bitstream."); } } os.init(og.serialno()); os.reset(); vi.init(); vc.init(); if (os.pagein(og) < 0) { // error; stream version mismatch perhaps throw new InternalException("Error reading first page of Ogg bitstream data."); } if (os.packetout(op) != 1) { // no page? must not be vorbis throw new InternalException("Error reading initial header packet."); } if (vi.synthesis_headerin(vc, op) < 0) { // error case; not a vorbis header throw new InternalException("This Ogg bitstream does not contain Vorbis audio data."); } int i = 0; while (i < 2) { while (i < 2) { if (checkState()) { return; } int result = oy.pageout(og); if (result == 0) break; // Need more data if (result == 1) { os.pagein(og); while (i < 2) { result = os.packetout(op); if (result == 0) break; if (result == -1) { throw new InternalException("Corrupt secondary header. Exiting."); } vi.synthesis_headerin(vc, op); i++; } } } index = oy.buffer(BUFSIZE); buffer = oy.data; try { bytes = bitStream.read(buffer, index, BUFSIZE); } catch (Exception e) { throw new InternalException(e); } if (bytes == 0 && i < 2) { throw new InternalException("End of file before finding all Vorbis headers!"); } oy.wrote(bytes); } convsize = BUFSIZE / vi.channels; vd.synthesis_init(vi); vb.init(vd); float[][][] _pcmf = new float[1][][]; int[] _index = new int[vi.channels]; getOutputLine(vi.channels, vi.rate); while (eos == 0) { while (eos == 0) { if (player != me) { return; } int result = oy.pageout(og); if (result == 0) break; // need more data if (result == -1) { // missing or corrupt data at this page // position // System.err.println("Corrupt or missing data in // bitstream; // continuing..."); } else { os.pagein(og); if (og.granulepos() == 0) { // chained = true; // eos = 1; // break; // } // while (true) { if (checkState()) { return; } result = os.packetout(op); if (result == 0) break; // need more data if (result == -1) { // missing or corrupt data at // this page position // no reason to complain; already complained // above // System.err.println("no reason to complain; // already complained above"); } else { // we have a packet. Decode it int samples; if (vb.synthesis(op) == 0) { // test for // success! vd.synthesis_blockin(vb); } while ((samples = vd.synthesis_pcmout(_pcmf, _index)) > 0) { if (checkState()) { return; } float[][] pcmf = _pcmf[0]; int bout = (samples < convsize ? samples : convsize); // convert doubles to 16 bit signed ints // (host order) and // interleave for (i = 0; i < vi.channels; i++) { int ptr = i * 2; // int ptr=i; int mono = _index[i]; for (int j = 0; j < bout; j++) { int val = (int) (pcmf[i][mono + j] * 32767.); if (val > 32767) { val = 32767; } if (val < -32768) { val = -32768; } if (val < 0) val = val | 0x8000; convbuffer[ptr] = (byte) (val); convbuffer[ptr + 1] = (byte) (val >>> 8); ptr += 2 * (vi.channels); } } outputLine.write(convbuffer, 0, 2 * vi.channels * bout); vd.synthesis_read(bout); } } } if (og.eos() != 0) eos = 1; } } if (eos == 0) { index = oy.buffer(BUFSIZE); buffer = oy.data; try { bytes = bitStream.read(buffer, index, BUFSIZE); } catch (Exception e) { throw new InternalException(e); } if (bytes == -1) { break; } oy.wrote(bytes); if (bytes == 0) eos = 1; } } os.clear(); vb.clear(); vd.clear(); vi.clear(); } oy.clear(); }
/** Reads headers and comments. */ private void readHeaders(HashMap aff_properties, HashMap af_properties) throws IOException { if (TDebug.TraceAudioConverter) TDebug.out("readHeaders("); index = oggSyncState_.buffer(bufferSize_); buffer = oggSyncState_.data; bytes = readFromStream(buffer, index, bufferSize_); if (bytes == -1) { if (TDebug.TraceAudioConverter) TDebug.out("Cannot get any data from selected Ogg bitstream."); throw new IOException("Cannot get any data from selected Ogg bitstream."); } oggSyncState_.wrote(bytes); if (oggSyncState_.pageout(oggPage_) != 1) { if (bytes < bufferSize_) { throw new IOException("EOF"); } if (TDebug.TraceAudioConverter) TDebug.out("Input does not appear to be an Ogg bitstream."); throw new IOException("Input does not appear to be an Ogg bitstream."); } oggStreamState_.init(oggPage_.serialno()); vorbisInfo.init(); vorbisComment.init(); aff_properties.put("ogg.serial", new Integer(oggPage_.serialno())); if (oggStreamState_.pagein(oggPage_) < 0) { // error; stream version mismatch perhaps if (TDebug.TraceAudioConverter) TDebug.out("Error reading first page of Ogg bitstream data."); throw new IOException("Error reading first page of Ogg bitstream data."); } if (oggStreamState_.packetout(oggPacket_) != 1) { // no page? must not be vorbis if (TDebug.TraceAudioConverter) TDebug.out("Error reading initial header packet."); throw new IOException("Error reading initial header packet."); } if (vorbisInfo.synthesis_headerin(vorbisComment, oggPacket_) < 0) { // error case; not a vorbis header if (TDebug.TraceAudioConverter) TDebug.out("This Ogg bitstream does not contain Vorbis audio data."); throw new IOException("This Ogg bitstream does not contain Vorbis audio data."); } int i = 0; while (i < 2) { while (i < 2) { int result = oggSyncState_.pageout(oggPage_); if (result == 0) { break; } // Need more data if (result == 1) { oggStreamState_.pagein(oggPage_); while (i < 2) { result = oggStreamState_.packetout(oggPacket_); if (result == 0) { break; } if (result == -1) { if (TDebug.TraceAudioConverter) TDebug.out("Corrupt secondary header. Exiting."); throw new IOException("Corrupt secondary header. Exiting."); } vorbisInfo.synthesis_headerin(vorbisComment, oggPacket_); i++; } } } index = oggSyncState_.buffer(bufferSize_); buffer = oggSyncState_.data; bytes = readFromStream(buffer, index, bufferSize_); if (bytes == -1) { break; } if (bytes == 0 && i < 2) { if (TDebug.TraceAudioConverter) TDebug.out("End of file before finding all Vorbis headers!"); throw new IOException("End of file before finding all Vorbis headers!"); } oggSyncState_.wrote(bytes); } // Read Ogg Vorbis comments. byte[][] ptr = vorbisComment.user_comments; String currComment = ""; int c = 0; for (int j = 0; j < ptr.length; j++) { if (ptr[j] == null) { break; } currComment = (new String(ptr[j], 0, ptr[j].length - 1, "UTF-8")).trim(); if (TDebug.TraceAudioConverter) TDebug.out(currComment); if (currComment.toLowerCase().startsWith("artist")) { aff_properties.put("author", currComment.substring(7)); } else if (currComment.toLowerCase().startsWith("title")) { aff_properties.put("title", currComment.substring(6)); } else if (currComment.toLowerCase().startsWith("album")) { aff_properties.put("album", currComment.substring(6)); } else if (currComment.toLowerCase().startsWith("date")) { aff_properties.put("date", currComment.substring(5)); } else if (currComment.toLowerCase().startsWith("copyright")) { aff_properties.put("copyright", currComment.substring(10)); } else if (currComment.toLowerCase().startsWith("comment")) { aff_properties.put("comment", currComment.substring(8)); } else if (currComment.toLowerCase().startsWith("genre")) { aff_properties.put("ogg.comment.genre", currComment.substring(6)); } else if (currComment.toLowerCase().startsWith("tracknumber")) { aff_properties.put("ogg.comment.track", currComment.substring(12)); } else { c++; aff_properties.put("ogg.comment.ext." + c, currComment); } aff_properties.put( "ogg.comment.encodedby", new String(vorbisComment.vendor, 0, vorbisComment.vendor.length - 1)); } }
/** * 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). * * @param readp read pointer * @return -1) hole in the data (lost packet)<br> * 0) need more date (only if readp==0)/eof<br> * 1) got a packet */ int processPacket(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 (decodeReady) { 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.synthesisPcmout(null, null); vd.synthesisBlockin(vb); samptrack += vd.synthesisPcmout(null, null) - oldsamples; bittrack += op.bytes * 8; // update the pcm offset. if (granulepos != -1 && op.endOfStream == 0) { int link = (seekable ? currentLink : 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.synthesisPcmout(null, null); granulepos -= samples; for (int i = 0; i < link; i++) { granulepos += pcmlengths[i]; } pcmOffset = granulepos; } return (1); } } } if (readp == 0) { return (0); } if (getNextPage(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.headerLen * 8; // has our decoding just traversed a bitstream boundary? if (decodeReady) { if (currentSerialno != og.serialno()) { decodeClear(); } } // 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 (!decodeReady) { int i; if (seekable) { currentSerialno = 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] == currentSerialno) { break; } } if (i == links) { // sign of a bogus stream. error out, // leave machine uninitialized return (-1); } currentLink = i; os.init(currentSerialno); os.reset(); } else { // we're streaming // fetch the three header packets, build the info struct int[] foo = new int[1]; int ret = fetchHeaders(vi[0], vc[0], foo, og); currentSerialno = foo[0]; if (ret != 0) { return ret; } currentLink++; i = 0; } makeDecodeReady(); } os.pagein(og); } }
/** * Uses the local ogg_stream storage in vf; this is important for non-streaming input sources. * * @param vi the info block * @param vc comment block * @param serialno serial numbers * @param ogPtr ogg pointer page * @return success codes OV_* */ int fetchHeaders(Info vi, Comment vc, int[] serialno, Page ogPtr) { Page og = new Page(); Packet op = new Packet(); int ret; if (ogPtr == null) { ret = getNextPage(og, CHUNKSIZE); if (ret == OV_EREAD) { return OV_EREAD; } if (ret < 0) { return OV_ENOTVORBIS; } ogPtr = og; } if (serialno != null) { serialno[0] = ogPtr.serialno(); } os.init(ogPtr.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(ogPtr); while (i < 3) { int result = os.packetout(op); if (result == 0) { break; } if (result == -1) { vi.clear(); vc.clear(); os.clear(); return -1; } if (vi.synthesisHeaderin(vc, op) != 0) { vi.clear(); vc.clear(); os.clear(); return -1; } i++; } if (i < 3) { if (getNextPage(ogPtr, 1) < 0) { vi.clear(); vc.clear(); os.clear(); return -1; } } } return 0; }
/** * Seek to a sample offset relative to the decompressed pcm stream. * * @param pos position * @return zero on success, nonzero on failure */ public int pcmSeek(long pos) { int link = -1; long total = pcmTotal(-1); if (!seekable) { return (-1); // don't dump machine if we can't seek } if (pos < 0 || pos > total) { // goto seek_error; pcmOffset = -1; decodeClear(); 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; } seekHelper(bisect); ret = getNextPage(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 (rawSeek(best) != 0) { // goto seek_error; pcmOffset = -1; decodeClear(); return -1; } // verify result if (pcmOffset >= pos) { // goto seek_error; pcmOffset = -1; decodeClear(); return -1; } if (pos > pcmTotal(-1)) { // goto seek_error; pcmOffset = -1; decodeClear(); return -1; } // discard samples until we reach the desired position. Crossing a // logical bitstream boundary with abandon is OK. while (pcmOffset < pos) { int target2 = (int) (pos - pcmOffset); float[][][] lPcm = new float[1][][]; int[] lIndex = new int[getInfo(-1).channels]; int samples = vd.synthesisPcmout(lPcm, lIndex); if (samples > target2) { samples = target2; } vd.synthesisRead(samples); pcmOffset += samples; if (samples < target2) { if (processPacket(1) == 0) { pcmOffset = pcmTotal(-1); // eof } } } return 0; // seek_error: // dump machine so we're in a known state // pcm_offset=-1; // decode_clear(); // return -1; }