/** * 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; } } } }
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; } } } }
/** @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; } }
@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; }
/* * 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(); }
/** * 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; }