/** * Buffers the given packet up ready for writing to the stream, but doesn't write it to disk yet. * The granule position is updated on the page. If writing the packet requires a new page, then * the updated granule position only applies to the new page */ public void bufferPacket(OggPacket packet, long granulePosition) { if (closed) { throw new IllegalStateException("Can't buffer packets on a closed stream!"); } if (!doneFirstPacket) { packet.setIsBOS(); doneFirstPacket = true; } int size = packet.getData().length; boolean emptyPacket = (size == 0); // Add to pages in turn OggPage page = getCurrentPage(false); int pos = 0; while (pos < size || emptyPacket) { pos = page.addPacket(packet, pos); if (pos < size) { page = getCurrentPage(true); page.setIsContinuation(); } page.setGranulePosition(granulePosition); emptyPacket = false; } currentGranulePosition = granulePosition; packet.setParent(page); }
/** * Returns the number of bytes (excluding headers) currently waiting to be written to disk. RFC * 3533 suggests that pages should normally be in the 4-8kb range. If this size exceeds just shy * of 64kb, then multiple pages will be needed in the underlying stream. */ public int getSizePendingFlush() { int size = 0; for (OggPage p : buffer) { size += p.getDataSize(); } return size; }
private OggPage getCurrentPage(boolean forceNew) { if (buffer.size() == 0 || forceNew) { OggPage page = new OggPage(sid, sequenceNumber++); if (currentGranulePosition > 0) { page.setGranulePosition(currentGranulePosition); } buffer.add(page); return page; } return buffer.get(buffer.size() - 1); }
/** * Returns the next packet in the file, or null if no more packets remain. Call {@link * OggPacket#isBeginningOfStream()} to detect if it is the first packet in the stream or not, and * use {@link OggPacket#getSid()} to track which stream it belongs to. */ public OggPacket getNextPacket() throws IOException { // If we skipped to a point in the stream, and // have a packet waiting, return that if (nextPacket != null) { OggPacket p = nextPacket; nextPacket = null; return p; } // If we're already part way through a page, // then fetch the next packet. If it's a // full one, then we're done. OggPacketData leftOver = null; if (it != null && it.hasNext()) { OggPacketData packet = it.next(); if (packet instanceof OggPacket) { return (OggPacket) packet; } leftOver = packet; } // Find the next page, from which // to get our next packet from int searched = 0; int pos = -1; boolean found = false; int r; while (searched < 65536 && !found) { r = inp.read(); if (r == -1) { // No more data return null; } switch (pos) { case -1: if (r == (int) 'O') { pos = 0; } break; case 0: if (r == (int) 'g') { pos = 1; } else { pos = -1; } break; case 1: if (r == (int) 'g') { pos = 2; } else { pos = -1; } break; case 2: if (r == (int) 'S') { found = true; } else { pos = -1; } break; } if (!found) { searched++; } } if (!found) { throw new IOException( "Next ogg packet header not found after searching " + searched + " bytes"); } searched -= 3; // OggS if (searched > 0) { System.err.println( "Warning - had to skip " + searched + " bytes of junk data before finding the next packet header"); } // Create the page, and prime the iterator on it try { OggPage page = new OggPage(inp); if (!page.isChecksumValid()) { System.err.println( "Warning - invalid checksum on page " + page.getSequenceNumber() + " of stream " + Integer.toHexString(page.getSid()) + " (" + page.getSid() + ")"); } it = page.getPacketIterator(leftOver); return getNextPacket(); } catch (EOFException eof) { System.err.println("Warning - data ended mid-page: " + eof.getMessage()); return null; } }
/** * Sets the current granule position. The granule position will be applied to all un-flushed * packets, and all future packets. As such, you should normally either call a flush just before * or just after this call. */ public void setGranulePosition(long position) { currentGranulePosition = position; for (OggPage p : buffer) { p.setGranulePosition(position); } }
/** * Returns the size of the page currently being written to, including its headers. For a new * stream, or a stream that has just been flushed, will return zero. * * @return Current page size, or 0 if no current page */ public int getCurrentPageSize() { if (buffer.isEmpty()) return 0; OggPage p = buffer.get(buffer.size() - 1); return p.getPageSize(); }