274 lines
6.9 KiB
Java
274 lines
6.9 KiB
Java
/* -*-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;
|
|
}
|
|
}
|