rockbox/songdbj/com/jcraft/jogg/StreamState.java

658 lines
18 KiB
Java
Raw Normal View History

/* 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;
//memset(os,0,sizeof(ogg_stream_state));
}
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;
//System.out.println("expand: body_fill="+body_fill+", body_storage="+body_data.length);
}
}
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){
// memmove(os->body_data,os->body_data+os->body_returned,
// os->body_fill*sizeof(char));
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;
//System.out.println("add: "+body_fill);
/* 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;
//System.out.println(this+" # body_returned="+body_returned);
body_returned+=bytes;
//System.out.println(this+"## body_returned="+body_returned);
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
//System.out.println("br="+br+", body_fill="+body_fill);
if(br!=0){
body_fill-=br;
if(body_fill!=0){
System.arraycopy(body_data, br, body_data, 0, body_fill);
}
body_returned=0;
}
//System.out.println("?? br="+br+", body_fill="+body_fill+" body_returned="+body_returned);
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;
}
}
}
}
//System.out.println("bodysize="+bodysize);
if(bodysize!=0){
body_expand(bodysize);
System.arraycopy(body_base, body, body_data, body_fill, bodysize);
body_fill+=bodysize;
}
//System.out.println("bodyfill="+body_fill);
{
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){
//System.out.println(this+" ---body_returned: "+body_returned);
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 */
//System.out.println("###body_returned: "+body_returned);
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;
//System.out.println("####body_returned: "+body_returned);
/* 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(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){ // overlap?
// System.arraycopy(body_data, body_returned, body_data, 0, body_fill);
// }
// body_returned=0;
// }
//
//System.out.println("pageout: e_o_s="+e_o_s+" lacing_fill="+lacing_fill+" body_fill="+body_fill+", lacing_fill="+lacing_fill+" b_o_s="+b_o_s);
//
// if((e_o_s!=0&&lacing_fill!=0) || /* 'were done, now flush' case */
// body_fill > 4096 || /* 'page nominal size' case */
// lacing_fill>=255 || /* 'segment table full' case */
// (lacing_fill!=0&&b_o_s==0)){ /* 'initial header page' case */
// int vals=0,bytes=0;
// int maxvals=(lacing_fill>255?255:lacing_fill);
// long acc=0;
// long pcm_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 */
// pcm_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;
// pcm_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(int i=6;i<14;i++){
// header[i]=(byte)pcm_pos;
// pcm_pos>>>=8;
// }
//
// /* 32 bits of stream serial number */
// {
// int serialn=serialno;
// for(int i=14;i<18;i++){
// header[i]=(byte)serialn;
// serialn>>>=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 pagen=pageno++;
// for(int i=18;i<22;i++){
// header[i]=(byte)pagen;
// pagen>>>=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(int i=0;i<vals;i++){
// header[i+27]=(byte)lacing_vals[i];
// bytes+=header[i+27]&0xff;
//// bytes+=header[i+27]=(lacing_vals[i]&0xff);
// }
//
// /* advance the lacing data and set the body_returned pointer */
//
// lacing_fill-=vals;
// System.arraycopy(lacing_vals, vals, lacing_vals, 0, lacing_fill);
// System.arraycopy(granule_vals, vals, granule_vals, 0, lacing_fill);
// body_returned=bytes;
//
// /* 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=0;
// og.body_len=bytes;
//
// /* calculate the checksum */
//
// og.checksum();
// return(1);
// }
// /* not enough data to construct a page and not end of stream */
// return(0);
//System.out.println("pageout: "+body_returned);
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);
}
}