Spaces:
Build error
Build error
/* -*-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; | |
} | |
} | |