/*
* THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.
*
* THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002
* by the Xiph.Org Foundation http://www.xiph.org/
*/
package org.xiph.libogg;
public class ogg_stream_state {
byte[] body_data; // unsigned char // bytes from packet bodies
int body_storage; // long //storage elements allocated
int body_fill; // long //elements stored; fill mark
int body_returned; // long //elements of fill returned
int[] lacing_vals; // The values that will go to the segment table
long[] granule_vals; // ogg_int64_t //granulepos values for headers. Not compact this way, but it is simple coupled to the lacing fifo
int lacing_storage; // long
int lacing_fill; // long
int lacing_packet; // long
int lacing_returned; // long
byte[] header; // unsigned char header[282]; //working space for header encode
int header_fill;
int e_o_s; //set when we have buffered the last packet in the logical bitstream
int b_o_s; //set after we've written the initial page of a logical bitstream
int serialno; // long
int pageno; // long
long packetno; // ogg_int64_t // sequence number for decode; the framing knows where there's a hole in the data, but we need coupling so
// that the codec (which is in a seperate abstraction layer) also knows about the gap
long granulepos; // ogg_int64_t
public ogg_stream_state() {
header = new byte[282];
}
public ogg_stream_state(int _serialno) {
header = new byte[282];
body_storage = 16 * 1024;
// body_data = _ogg_malloc(os->body_storage*sizeof(*os->body_data));
body_data = new byte[body_storage];
lacing_storage = 1024;
lacing_vals = new int[lacing_storage];
granule_vals = new long[lacing_storage];
serialno = _serialno;
}
public int getSerialNo() {
return serialno;
}
// Helpers for ogg_stream_encode; this keeps the structure and what's happening fairly clear
public void _os_body_expand(int needed) {
if (body_storage <= body_fill + needed) {
body_storage += (needed + 1024);
// body_data = _ogg_realloc(os->body_data,os->body_storage*sizeof(*os->body_data));
byte[] temp = new byte[body_storage];
System.arraycopy(body_data, 0, temp, 0, body_data.length);
body_data = temp;
}
}
public void _os_lacing_expand(int needed) {
if (lacing_storage <= lacing_fill + needed) {
lacing_storage += (needed + 32);
// lacing_vals = _ogg_realloc(os->lacing_vals,os->lacing_storage*sizeof(*os->lacing_vals));
int[] temp = new int[lacing_storage];
System.arraycopy(lacing_vals, 0, temp, 0, lacing_vals.length);
lacing_vals = temp;
// granule_vals = _ogg_realloc(os->granule_vals,os->lacing_storage*sizeof(*os->granule_vals));
long[] temp2 = new long[lacing_storage];
System.arraycopy(granule_vals, 0, temp, 0, granule_vals.length);
granule_vals = temp2;
}
}
public boolean ogg_stream_packetin(ogg_packet op) {
// OOP varaible lacing_vals renamed lacing_vals_local
int lacing_vals_local = op.bytes / 255 + 1;
int i;
if (body_returned > 0) {
/* advance packet data according to the body_returned pointer. We
* had to keep it around to return a pointer into the buffer last call */
body_fill -= body_returned;
if (body_fill > 0) {
// memmove( body_data, body_data + body_returned, body_fill );
System.arraycopy(body_data, body_returned, body_data, 0, body_fill);
}
body_returned = 0;
}
// make sure we have the buffer storage
_os_body_expand(op.bytes);
_os_lacing_expand(lacing_vals_local);
/* Copy in the submitted packet. Yes, the copy is a waste; this is
* the liability of overly clean abstraction for the time being. It
* will actually be fairly easy to eliminate the extra copy in the future */
// memcpy( body_data + body_fill, op.packet, op.bytes );
System.arraycopy(op.packet, 0, body_data, body_fill, op.bytes);
body_fill += op.bytes;
// Store lacing vals for this packet
for (i = 0; i < lacing_vals_local - 1; i++) {
lacing_vals[lacing_fill + i] = 255;
granule_vals[lacing_fill + i] = granulepos;
}
lacing_vals[lacing_fill + i] = (op.bytes) % 255;
granulepos = granule_vals[lacing_fill + i] = op.granulepos;
// flag the first segment as the beginning of the packet
lacing_vals[lacing_fill] |= 0x100;
lacing_fill += lacing_vals_local;
// for the sake of completeness
packetno++;
if (op.e_o_s > 0)
e_o_s = 1;
return true;
}
/* This will flush remaining packets into a page (returning nonzero),
* even if there is not enough data to trigger a flush normally
* (undersized page). If there are no packets or partial packets to
* flush, ogg_stream_flush returns 0. Note that ogg_stream_flush will
* try to flush a normal sized page like ogg_stream_pageout; a call to
* ogg_stream_flush does not guarantee that all packets have flushed.
* Only a return value of 0 from ogg_stream_flush indicates all packet
* data is flushed into pages.
*
* since ogg_stream_flush will flush the last page in a stream even if
* it's undersized, you almost certainly want to use ogg_stream_pageout
* (and *not* ogg_stream_flush) unless you specifically need to flush
* an page regardless of size in the middle of a stream. */
public boolean ogg_stream_flush(ogg_page og) {
int maxvals = (lacing_fill > 255 ? 255 : lacing_fill);
if (maxvals == 0)
return false;
int i;
int vals = 0;
int bytes = 0;
int acc = 0;
// ogg_int64_t granule_pos=os->granule_vals[0];
long granule_pos = granule_vals[0];
// construct a page
// decide how many segments to include
// If this is the initial header case, the first page must only include the initial header packet
if (b_o_s == 0) { // 'initial header page' case
granule_pos = 0;
for (vals = 0; vals < maxvals; vals++) {
if ((lacing_vals[vals] & 0x0ff) < 255) {
vals++;
break;
}
}
} else {
for (vals = 0; vals < maxvals; vals++) {
if (acc > 4096)
break;
acc += lacing_vals[vals] & 0x0ff;
granule_pos = granule_vals[vals];
}
}
// construct the header in temp storage
// memcpy(os->header,"OggS",4);
// header[0] = 'O';
// header[1] = 'g';
// header[2] = 'g';
// header[3] = 'S';
System.arraycopy("OggS".getBytes(), 0, header, 0, 4);
// stream structure version
header[4] = 0x00;
// continued packet flag?
header[5] = 0x00;
if ((lacing_vals[0] & 0x100) == 0)
header[5] |= 0x01;
// first page flag?
if (b_o_s == 0)
header[5] |= 0x02;
// last page flag?
if ((e_o_s > 0) && (lacing_fill == vals))
header[5] |= 0x04;
b_o_s = 1;
// 64 bits of PCM position
for (i = 6; i < 14; i++) {
header[i] = (byte) (granule_pos);
granule_pos >>>= 8;
}
// 32 bits of stream serial number
// local OOP varname serialno to serialno_local
int serialno_local = serialno;
for (i = 14; i < 18; i++) {
header[i] = (byte) (serialno_local);
serialno_local >>>= 8;
}
// 32 bits of page counter (we have both counter and page header because this val can roll over)
if (pageno == -1)
pageno = 0;
/* because someone called stream_reset; this would be a strange thing to do in an
* encode stream, but it has plausible uses */
// local OOP varname serialno to serialno_local
int pageno_local = pageno++;
for (i = 18; i < 22; i++) {
header[i] = (byte) (pageno_local);
pageno_local >>>= 8;
}
// zero for computation; filled in later
header[22] = 0;
header[23] = 0;
header[24] = 0;
header[25] = 0;
// segment table
header[26] = (byte) (vals);
for (i = 0; i < vals; i++) {
header[i + 27] = (byte) (lacing_vals[i]);
bytes += (header[i + 27] & 0xff);
}
// this has been hacked together
// do not know if pointers (offsets) are ok
// or if actual data needs to be copied to ogg_page
// set pointers in the ogg_page struct
og.header = header;
og.header_len = header_fill = vals + 27;
// og.body = body_data + body_returned;
og.body = new byte[body_data.length - body_returned];
System.arraycopy(body_data, body_returned, og.body, 0, body_data.length - body_returned);
og.body_len = bytes;
// advance the lacing data and set the body_returned pointer
lacing_fill -= vals;
// memmove(os->lacing_vals, os->lacing_vals+vals, os->lacing_fill*sizeof(*os->lacing_vals));
// int[] temp = new int[ lacing_fill ];
// System.arraycopy( lacing_vals, vals, temp, 0, lacing_fill );
// lacing_vals = temp;
// System.arraycopy( lacing_vals, vals, lacing_vals, 0, lacing_fill*4 );
System.arraycopy(lacing_vals, vals, lacing_vals, 0, lacing_fill);
// memmove(os->granule_vals, os->granule_vals+vals, os->lacing_fill*sizeof(*os->granule_vals));
// long[] temp2 = new long[ lacing_fill ];
// System.arraycopy( granule_vals, vals, temp2, 0, lacing_fill );
// granule_vals= temp2;
// System.arraycopy( granule_vals, vals, granule_vals, 0, lacing_fill*8 );
System.arraycopy(granule_vals, vals, granule_vals, 0, lacing_fill);
body_returned += bytes;
// calculate the checksum
og.ogg_page_checksum_set();
return true;
}
/* This constructs pages from buffered packet segments. The pointers
* returned are to static buffers; do not free. The returned buffers are
* good only until the next call (using the same ogg_stream_state) */
public boolean ogg_stream_pageout(ogg_page og) {
if ((e_o_s > 0 && lacing_fill > 0) || // 'were done, now flush' case
body_fill - body_returned > 4096 || // 'page nominal size' case
lacing_fill >= 255 || // 'segment table full' case
(lacing_fill > 0 && b_o_s <= 0)) { // 'initial header page' case
return ogg_stream_flush(og);
}
// not enough data to construct a page and not end of stream
return false;
}
}