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; | |
public class StreamState { | |
byte[] body_data; /* bytes from packet bodies */ | |
int body_storage; /* storage elements allocated */ | |
int body_fill; /* elements stored; fill mark */ | |
private int body_returned; /* elements of fill returned */ | |
int[] lacing_vals; /* The values that will go to the segment table */ | |
long[] granule_vals; /* | |
* pcm_pos values for headers. Not compact this way, but it is simple coupled to | |
* the lacing fifo | |
*/ | |
int lacing_storage; | |
int lacing_fill; | |
int lacing_packet; | |
int lacing_returned; | |
byte[] header = new byte[282]; /* working space for header encode */ | |
int header_fill; | |
public 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; | |
int pageno; | |
long packetno; /* | |
* 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; | |
public StreamState() { | |
init(); | |
} | |
StreamState(int serialno) { | |
this(); | |
init(serialno); | |
} | |
void init() { | |
body_storage = 16 * 1024; | |
body_data = new byte[body_storage]; | |
lacing_storage = 1024; | |
lacing_vals = new int[lacing_storage]; | |
granule_vals = new long[lacing_storage]; | |
} | |
public void init(int serialno) { | |
if (body_data == null) { | |
init(); | |
} else { | |
for (int i = 0; i < body_data.length; i++) | |
body_data[i] = 0; | |
for (int i = 0; i < lacing_vals.length; i++) | |
lacing_vals[i] = 0; | |
for (int i = 0; i < granule_vals.length; i++) | |
granule_vals[i] = 0; | |
} | |
this.serialno = serialno; | |
} | |
public void clear() { | |
body_data = null; | |
lacing_vals = null; | |
granule_vals = null; | |
} | |
void destroy() { | |
clear(); | |
} | |
void body_expand(int needed) { | |
if (body_storage <= body_fill + needed) { | |
body_storage += (needed + 1024); | |
byte[] foo = new byte[body_storage]; | |
System.arraycopy(body_data, 0, foo, 0, body_data.length); | |
body_data = foo; | |
} | |
} | |
void lacing_expand(int needed) { | |
if (lacing_storage <= lacing_fill + needed) { | |
lacing_storage += (needed + 32); | |
int[] foo = new int[lacing_storage]; | |
System.arraycopy(lacing_vals, 0, foo, 0, lacing_vals.length); | |
lacing_vals = foo; | |
long[] bar = new long[lacing_storage]; | |
System.arraycopy(granule_vals, 0, bar, 0, granule_vals.length); | |
granule_vals = bar; | |
} | |
} | |
/* submit data to the internal buffer of the framing engine */ | |
public int packetin(Packet op) { | |
int lacing_val = op.bytes / 255 + 1; | |
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) { | |
System.arraycopy(body_data, body_returned, body_data, 0, body_fill); | |
} | |
body_returned = 0; | |
} | |
/* make sure we have the buffer storage */ | |
body_expand(op.bytes); | |
lacing_expand(lacing_val); | |
/* | |
* 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 | |
*/ | |
System.arraycopy(op.packet_base, op.packet, body_data, body_fill, op.bytes); | |
body_fill += op.bytes; | |
/* Store lacing vals for this packet */ | |
int j; | |
for (j = 0; j < lacing_val - 1; j++) { | |
lacing_vals[lacing_fill + j] = 255; | |
granule_vals[lacing_fill + j] = granulepos; | |
} | |
lacing_vals[lacing_fill + j] = (op.bytes) % 255; | |
granulepos = granule_vals[lacing_fill + j] = op.granulepos; | |
/* flag the first segment as the beginning of the packet */ | |
lacing_vals[lacing_fill] |= 0x100; | |
lacing_fill += lacing_val; | |
/* for the sake of completeness */ | |
packetno++; | |
if (op.e_o_s != 0) | |
e_o_s = 1; | |
return (0); | |
} | |
public int packetout(Packet op) { | |
/* | |
* The last part of decode. We have the stream broken into packet segments. Now | |
* we need to group them into packets (or return the out of sync markers) | |
*/ | |
int ptr = lacing_returned; | |
if (lacing_packet <= ptr) { | |
return (0); | |
} | |
if ((lacing_vals[ptr] & 0x400) != 0) { | |
/* We lost sync here; let the app know */ | |
lacing_returned++; | |
/* | |
* we need to tell the codec there's a gap; it might need to handle previous | |
* packet dependencies. | |
*/ | |
packetno++; | |
return (-1); | |
} | |
/* Gather the whole packet. We'll have no holes or a partial packet */ | |
{ | |
int size = lacing_vals[ptr] & 0xff; | |
int bytes = 0; | |
op.packet_base = body_data; | |
op.packet = body_returned; | |
op.e_o_s = lacing_vals[ptr] & 0x200; /* last packet of the stream? */ | |
op.b_o_s = lacing_vals[ptr] & 0x100; /* first packet of the stream? */ | |
bytes += size; | |
while (size == 255) { | |
int val = lacing_vals[++ptr]; | |
size = val & 0xff; | |
if ((val & 0x200) != 0) | |
op.e_o_s = 0x200; | |
bytes += size; | |
} | |
op.packetno = packetno; | |
op.granulepos = granule_vals[ptr]; | |
op.bytes = bytes; | |
body_returned += bytes; | |
lacing_returned = ptr + 1; | |
} | |
packetno++; | |
return (1); | |
} | |
// add the incoming page to the stream state; we decompose the page | |
// into packet segments here as well. | |
public int pagein(Page og) { | |
byte[] header_base = og.header_base; | |
int header = og.header; | |
byte[] body_base = og.body_base; | |
int body = og.body; | |
int bodysize = og.body_len; | |
int segptr = 0; | |
int version = og.version(); | |
int continued = og.continued(); | |
int bos = og.bos(); | |
int eos = og.eos(); | |
long granulepos = og.granulepos(); | |
int _serialno = og.serialno(); | |
int _pageno = og.pageno(); | |
int segments = header_base[header + 26] & 0xff; | |
// clean up 'returned data' | |
{ | |
int lr = lacing_returned; | |
int br = body_returned; | |
// body data | |
if (br != 0) { | |
body_fill -= br; | |
if (body_fill != 0) { | |
System.arraycopy(body_data, br, body_data, 0, body_fill); | |
} | |
body_returned = 0; | |
} | |
if (lr != 0) { | |
// segment table | |
if ((lacing_fill - lr) != 0) { | |
System.arraycopy(lacing_vals, lr, lacing_vals, 0, lacing_fill - lr); | |
System.arraycopy(granule_vals, lr, granule_vals, 0, lacing_fill - lr); | |
} | |
lacing_fill -= lr; | |
lacing_packet -= lr; | |
lacing_returned = 0; | |
} | |
} | |
// check the serial number | |
if (_serialno != serialno) | |
return (-1); | |
if (version > 0) | |
return (-1); | |
lacing_expand(segments + 1); | |
// are we in sequence? | |
if (_pageno != pageno) { | |
int i; | |
// unroll previous partial packet (if any) | |
for (i = lacing_packet; i < lacing_fill; i++) { | |
body_fill -= lacing_vals[i] & 0xff; | |
// System.out.println("??"); | |
} | |
lacing_fill = lacing_packet; | |
// make a note of dropped data in segment table | |
if (pageno != -1) { | |
lacing_vals[lacing_fill++] = 0x400; | |
lacing_packet++; | |
} | |
// are we a 'continued packet' page? If so, we'll need to skip | |
// some segments | |
if (continued != 0) { | |
bos = 0; | |
for (; segptr < segments; segptr++) { | |
int val = (header_base[header + 27 + segptr] & 0xff); | |
body += val; | |
bodysize -= val; | |
if (val < 255) { | |
segptr++; | |
break; | |
} | |
} | |
} | |
} | |
if (bodysize != 0) { | |
body_expand(bodysize); | |
System.arraycopy(body_base, body, body_data, body_fill, bodysize); | |
body_fill += bodysize; | |
} | |
{ | |
int saved = -1; | |
while (segptr < segments) { | |
int val = (header_base[header + 27 + segptr] & 0xff); | |
lacing_vals[lacing_fill] = val; | |
granule_vals[lacing_fill] = -1; | |
if (bos != 0) { | |
lacing_vals[lacing_fill] |= 0x100; | |
bos = 0; | |
} | |
if (val < 255) | |
saved = lacing_fill; | |
lacing_fill++; | |
segptr++; | |
if (val < 255) | |
lacing_packet = lacing_fill; | |
} | |
/* set the granulepos on the last pcmval of the last full packet */ | |
if (saved != -1) { | |
granule_vals[saved] = granulepos; | |
} | |
} | |
if (eos != 0) { | |
e_o_s = 1; | |
if (lacing_fill > 0) | |
lacing_vals[lacing_fill - 1] |= 0x200; | |
} | |
pageno = _pageno + 1; | |
return (0); | |
} | |
/* | |
* 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 gurantee that all | |
* packets have flushed. Only a return value of 0 from ogg_stream_flush | |
* indicates all packet data is flushed into pages. | |
* | |
* ogg_stream_page 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 need to flush an undersized page in the middle | |
* of a stream for some reason. | |
*/ | |
public int flush(Page og) { | |
int i; | |
int vals = 0; | |
int maxvals = (lacing_fill > 255 ? 255 : lacing_fill); | |
int bytes = 0; | |
int acc = 0; | |
long granule_pos = granule_vals[0]; | |
if (maxvals == 0) | |
return (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 */ | |
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 */ | |
{ | |
int _serialno = serialno; | |
for (i = 14; i < 18; i++) { | |
header[i] = (byte) _serialno; | |
_serialno >>>= 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 | |
*/ | |
{ | |
int _pageno = pageno++; | |
for (i = 18; i < 22; i++) { | |
header[i] = (byte) _pageno; | |
_pageno >>>= 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); | |
} | |
/* set pointers in the ogg_page struct */ | |
og.header_base = header; | |
og.header = 0; | |
og.header_len = header_fill = vals + 27; | |
og.body_base = body_data; | |
og.body = body_returned; | |
og.body_len = bytes; | |
/* advance the lacing data and set the body_returned pointer */ | |
lacing_fill -= vals; | |
System.arraycopy(lacing_vals, vals, lacing_vals, 0, lacing_fill * 4); | |
System.arraycopy(granule_vals, vals, granule_vals, 0, lacing_fill * 8); | |
body_returned += bytes; | |
/* calculate the checksum */ | |
og.checksum(); | |
/* done */ | |
return (1); | |
} | |
/* | |
* 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 int pageout(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 flush(og); | |
} | |
return 0; | |
} | |
public int eof() { | |
return e_o_s; | |
} | |
public int reset() { | |
body_fill = 0; | |
body_returned = 0; | |
lacing_fill = 0; | |
lacing_packet = 0; | |
lacing_returned = 0; | |
header_fill = 0; | |
e_o_s = 0; | |
b_o_s = 0; | |
pageno = -1; | |
packetno = 0; | |
granulepos = 0; | |
return (0); | |
} | |
} | |