/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ /* JOrbis * Copyright (C) 2000 ymnk, JCraft,Inc. * * Written by: 2000 ymnk<ymnk@jcraft.com> * * Many thanks to * Monty <monty@xiph.org> and * The XIPHOPHORUS Company http://www.xiph.org/ . * JOrbis has been based on their awesome works, Vorbis codec. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public License * as published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package com.jcraft.jogg; // DECODING PRIMITIVES: packet streaming layer // This has two layers to place more of the multi-serialno and paging // control in the application's hands. First, we expose a data buffer // using ogg_decode_buffer(). The app either copies into the // buffer, or passes it directly to read(), etc. We then call // ogg_decode_wrote() to tell how many bytes we just added. // // Pages are returned (pointers into the buffer in ogg_sync_state) // by ogg_decode_stream(). The page is then submitted to // ogg_decode_page() along with the appropriate // ogg_stream_state* (ie, matching serialno). We then get raw // packets out calling ogg_stream_packet() with a // ogg_stream_state. See the 'frame-prog.txt' docs for details and // example code. public class SyncState { public byte[] data; int storage; int fill; int returned; int unsynced; int headerbytes; int bodybytes; public int clear() { data = null; return (0); } public int buffer(int size) { // first, clear out any space that has been previously returned if (returned != 0) { fill -= returned; if (fill > 0) { System.arraycopy(data, returned, data, 0, fill); } returned = 0; } if (size > storage - fill) { // We need to extend the internal buffer int newsize = size + fill + 4096; // an extra page to be nice if (data != null) { byte[] foo = new byte[newsize]; System.arraycopy(data, 0, foo, 0, data.length); data = foo; } else { data = new byte[newsize]; } storage = newsize; } return (fill); } public int wrote(int bytes) { if (fill + bytes > storage) return (-1); fill += bytes; return (0); } // sync the stream. This is meant to be useful for finding page // boundaries. // // return values for this: // -n) skipped n bytes // 0) page not ready; more data (no bytes skipped) // n) page synced at current location; page length n bytes private Page pageseek = new Page(); private byte[] chksum = new byte[4]; 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 synchronized (chksum) { // Grab the checksum bytes, set the header field to zero System.arraycopy(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; 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 System.arraycopy(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); } } // 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 } } // clear things to an initial state. Good to call, eg, before seeking public int reset() { fill = 0; returned = 0; unsynced = 0; headerbytes = 0; bodybytes = 0; return (0); } public void init() { } public int getDataOffset() { return returned; } public int getBufferOffset() { return fill; } }