/******************************************************************** * * * THIS FILE IS PART OF THE Ogg CONTAINER 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-2018 * * by the Xiph.Org Foundation http://www.xiph.org/ * * * ********************************************************************
function: code raw packets into framed OggSquish stream and decode Ogg streams back into raw packets
note: The CRC code is directly derived from public domain code by Ross Williams (ross@guest.adelaide.edu.au). See docs/framing.html for details.
/* returns the number of packets that are completed on this page (if the leading packet is begun on a previous page, but ends on this
page, it's counted */
/* NOTE: If a page consists of a packet begun on a previous page, and a new packet begun (but not completed) on this page, the return will be: ogg_page_packets(page) ==1, ogg_page_continued(page) !=0
If a page happens to be a single packet that was begun on a previous page, and spans to the next page (in the case of a three or more page packet), the return will be: ogg_page_packets(page) ==0, ogg_page_continued(page) !=0
*/
int ogg_page_packets(const ogg_page *og){ int i,n=og->header[26],count=0; for(i=0;i<n;i++) if(og->header[27+i]<255)count++; return(count);
}
#if 0 /* helper to initialize lookup for direct-table CRC (illustrative; we
use the static init in crctable.h) */
staticvoid _ogg_crc_init(){ int i, j;
ogg_uint32_t polynomial, crc;
polynomial = 0x04c11db7; /* The same as the ethernet generator polynomial, although we use an unreflected alg and an init/final
of 0, not 0xffffffff */ for (i = 0; i <= 0xFF; i++){
crc = i << 24;
/* async/delayed error detection for the ogg_stream_state */ int ogg_stream_check(ogg_stream_state *os){ if(!os || !os->body_data) return -1; return 0;
}
/* _clear does not free os, only the non-flat storage within */ int ogg_stream_clear(ogg_stream_state *os){ if(os){ if(os->body_data)_ogg_free(os->body_data); if(os->lacing_vals)_ogg_free(os->lacing_vals); if(os->granule_vals)_ogg_free(os->granule_vals);
memset(os,0,sizeof(*os));
} return(0);
}
int ogg_stream_destroy(ogg_stream_state *os){ if(os){
ogg_stream_clear(os);
_ogg_free(os);
} return(0);
}
/* Helpers for ogg_stream_encode; this keeps the structure and
what's happening fairly clear */
/* checksum the page */ /* Direct table CRC; note that this will be faster in the future if we
perform the checksum simultaneously with other copies */
static ogg_uint32_t _os_update_crc(ogg_uint32_t crc, unsignedchar *buffer, int size){ while (size>=8){
crc^=((ogg_uint32_t)buffer[0]<<24)|((ogg_uint32_t)buffer[1]<<16)|((ogg_uint32_t)buffer[2]<<8)|((ogg_uint32_t)buffer[3]);
/* submit data to the internal buffer of the framing engine */ int ogg_stream_iovecin(ogg_stream_state *os, ogg_iovec_t *iov, int count, long e_o_s, ogg_int64_t granulepos){
for (i = 0; i < count; ++i){ if(iov[i].iov_len>LONG_MAX) return -1; if(bytes>LONG_MAX-(long)iov[i].iov_len) return -1;
bytes += (long)iov[i].iov_len;
}
lacing_vals=bytes/255+1;
if(os->body_returned){ /* advance packet data according to the body_returned pointer. We had to keep it around to return a pointer into the buffer last
call */
/* make sure we have the buffer storage */ if(_os_body_expand(os,bytes) || _os_lacing_expand(os,lacing_vals)) return -1;
/* 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 */
for (i = 0; i < count; ++i) {
memcpy(os->body_data+os->body_fill, iov[i].iov_base, iov[i].iov_len);
os->body_fill += (int)iov[i].iov_len;
}
/* Store lacing vals for this packet */ for(i=0;i<lacing_vals-1;i++){
os->lacing_vals[os->lacing_fill+i]=255;
os->granule_vals[os->lacing_fill+i]=os->granulepos;
}
os->lacing_vals[os->lacing_fill+i]=bytes%255;
os->granulepos=os->granule_vals[os->lacing_fill+i]=granulepos;
/* flag the first segment as the beginning of the packet */
os->lacing_vals[os->lacing_fill]|= 0x100;
os->lacing_fill+=lacing_vals;
/* for the sake of completeness */
os->packetno++;
/* Conditionally flush a page; force==0 will only flush nominal-size pages, force==1 forces us to flush a page regardless of page size
so long as there's any data available at all. */ staticint ogg_stream_flush_i(ogg_stream_state *os,ogg_page *og, int force, int nfill){ int i; int vals=0; int maxvals=(os->lacing_fill>255?255:os->lacing_fill); int bytes=0; long acc=0;
ogg_int64_t granule_pos=-1;
/* 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(os->b_o_s==0){ /* 'initial header page' case */
granule_pos=0; for(vals=0;vals<maxvals;vals++){ if((os->lacing_vals[vals]&0x0ff)<255){
vals++; break;
}
}
}else{
/* The extra packets_done, packet_just_done logic here attempts to do two things: 1) Don't unnecessarily span pages. 2) Unless necessary, don't flush pages if there are less than four packets on them; this expands page size to reduce unnecessary overhead if incoming packets are large. These are not necessary behaviors, just 'always better than naive flushing' without requiring an application to explicitly request a specific optimized
behavior. We'll want an explicit behavior setup pathway eventually as well. */
int packets_done=0; int packet_just_done=0; for(vals=0;vals<maxvals;vals++){ if(acc>nfill && packet_just_done>=4){
force=1; break;
}
acc+=os->lacing_vals[vals]&0x0ff; if((os->lacing_vals[vals]&0xff)<255){
granule_pos=os->granule_vals[vals];
packet_just_done=++packets_done;
}else
packet_just_done=0;
} if(vals==255)force=1;
}
if(!force) return(0);
/* construct the header in temp storage */
memcpy(os->header,"OggS",4);
/* stream structure version */
os->header[4]=0x00;
/* continued packet flag? */
os->header[5]=0x00; if((os->lacing_vals[0]&0x100)==0)os->header[5]|=0x01; /* first page flag? */ if(os->b_o_s==0)os->header[5]|=0x02; /* last page flag? */ if(os->e_o_s && os->lacing_fill==vals)os->header[5]|=0x04;
os->b_o_s=1;
/* 64 bits of PCM position */ for(i=6;i<14;i++){
os->header[i]=(unsignedchar)(granule_pos&0xff);
granule_pos>>=8;
}
/* 32 bits of stream serial number */
{ long serialno=os->serialno; for(i=14;i<18;i++){
os->header[i]=(unsignedchar)(serialno&0xff);
serialno>>=8;
}
}
/* 32 bits of page counter (we have both counter and page header
because this val can roll over) */ if(os->pageno==-1)os->pageno=0; /* because someone called stream_reset; this would be a strange thing to do in an encode stream, but it has
plausible uses */
{ long pageno=os->pageno++; for(i=18;i<22;i++){
os->header[i]=(unsignedchar)(pageno&0xff);
pageno>>=8;
}
}
/* zero for computation; filled in later */
os->header[22]=0;
os->header[23]=0;
os->header[24]=0;
os->header[25]=0;
/* set pointers in the ogg_page struct */
og->header=os->header;
og->header_len=os->header_fill=vals+27;
og->body=os->body_data+os->body_returned;
og->body_len=bytes;
/* advance the lacing data and set the body_returned pointer */
/* 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
a page regardless of size in the middle of a stream. */
int ogg_stream_flush(ogg_stream_state *os,ogg_page *og){ return ogg_stream_flush_i(os,og,1,4096);
}
/* Like the above, but an argument is provided to adjust the nominal page size for applications which are smart enough to provide their
own delay based flushing */
int ogg_stream_flush_fill(ogg_stream_state *os,ogg_page *og, int nfill){ return ogg_stream_flush_i(os,og,1,nfill);
}
/* 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) */
int ogg_stream_pageout(ogg_stream_state *os, ogg_page *og){ int force=0; if(ogg_stream_check(os)) return 0;
if((os->e_o_s&&os->lacing_fill) || /* 'were done, now flush' case */
(os->lacing_fill&&!os->b_o_s)) /* 'initial header page' case */
force=1;
return(ogg_stream_flush_i(os,og,force,4096));
}
/* Like the above, but an argument is provided to adjust the nominal page size for applications which are smart enough to provide their
own delay based flushing */
int ogg_stream_pageout_fill(ogg_stream_state *os, ogg_page *og, int nfill){ int force=0; if(ogg_stream_check(os)) return 0;
if((os->e_o_s&&os->lacing_fill) || /* 'were done, now flush' case */
(os->lacing_fill&&!os->b_o_s)) /* 'initial header page' case */
force=1;
return(ogg_stream_flush_i(os,og,force,nfill));
}
int ogg_stream_eos(ogg_stream_state *os){ if(ogg_stream_check(os)) return 1; return os->e_o_s;
}
/* 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_sync_buffer(). The app either copies into the buffer, or passes it directly to read(), etc. We then call ogg_sync_wrote() to tell how many bytes we just added.
Pages are returned (pointers into the buffer in ogg_sync_state) by ogg_sync_pageout(). The page is then submitted to ogg_stream_pagein() along with the appropriate ogg_stream_state* (ie, matching serialno). We then get raw packets out calling ogg_stream_packetout() with a
ogg_stream_state. */
/* initialize the struct to a known state */ int ogg_sync_init(ogg_sync_state *oy){ if(oy){
oy->storage = -1; /* used as a readiness flag */
memset(oy,0,sizeof(*oy));
} return(0);
}
/* clear non-flat storage within */ int ogg_sync_clear(ogg_sync_state *oy){ if(oy){ if(oy->data)_ogg_free(oy->data);
memset(oy,0,sizeof(*oy));
} return(0);
}
int ogg_sync_destroy(ogg_sync_state *oy){ if(oy){
ogg_sync_clear(oy);
_ogg_free(oy);
} return(0);
}
int ogg_sync_check(ogg_sync_state *oy){ if(oy->storage<0) return -1; return 0;
}
char *ogg_sync_buffer(ogg_sync_state *oy, long size){ if(ogg_sync_check(oy)) return NULL;
/* first, clear out any space that has been previously returned */ if(oy->returned){
oy->fill-=oy->returned; if(oy->fill>0)
memmove(oy->data,oy->data+oy->returned,oy->fill);
oy->returned=0;
}
if(size>oy->storage-oy->fill){ /* We need to extend the internal buffer */ long newsize; void *ret;
if(size>INT_MAX-4096-oy->fill){
ogg_sync_clear(oy); return NULL;
}
newsize=size+oy->fill+4096; /* an extra page to be nice */ if(oy->data)
ret=_ogg_realloc(oy->data,newsize); else
ret=_ogg_malloc(newsize); if(!ret){
ogg_sync_clear(oy); return NULL;
}
oy->data=ret;
oy->storage=newsize;
}
/* expose a segment at least as large as requested at the fill mark */ return((char *)oy->data+oy->fill);
}
int ogg_sync_wrote(ogg_sync_state *oy, long bytes){ if(ogg_sync_check(oy))return -1; if(oy->fill+bytes>oy->storage)return -1;
oy->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
*/
long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og){ unsignedchar *page=oy->data+oy->returned; unsignedchar *next; long bytes=oy->fill-oy->returned;
if(ogg_sync_check(oy))return 0;
if(oy->headerbytes==0){ int headerbytes,i; if(bytes<27)return(0); /* not enough for a header */
/* The whole test page is buffered. Verify the checksum */
{ /* Grab the checksum bytes, set the header field to zero */ char chksum[4];
ogg_page log;
memcpy(chksum,page+22,4);
memset(page+22,0,4);
/* set up a temp page struct and recompute the checksum */
log.header=page;
log.header_len=oy->headerbytes;
log.body=page+oy->headerbytes;
log.body_len=oy->bodybytes;
ogg_page_checksum_set(&log);
/* Compare */ if(memcmp(chksum,page+22,4)){ /* D'oh. Mismatch! Corrupt page (or miscapture and not a page
at all) */ /* replace the computed checksum with the one actually read in */
memcpy(page+22,chksum,4);
/* yes, have a whole page all ready to go */
{ if(og){
og->header=page;
og->header_len=oy->headerbytes;
og->body=page+oy->headerbytes;
og->body_len=oy->bodybytes;
}
/* sync the stream and get a page. Keep trying until we find a page. Suppress '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 */
int ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og){
if(ogg_sync_check(oy))return 0;
/* 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 */
for(;;){ long ret=ogg_sync_pageseek(oy,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(!oy->unsynced){
oy->unsynced=1; return(-1);
}
/* loop. keep looking */
}
}
/* add the incoming page to the stream state; we decompose the page
into packet segments here as well. */
int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og){ unsignedchar *header=og->header; unsignedchar *body=og->body; long bodysize=og->body_len; int segptr=0;
int version=ogg_page_version(og); int continued=ogg_page_continued(og); int bos=ogg_page_bos(og); int eos=ogg_page_eos(og);
ogg_int64_t granulepos=ogg_page_granulepos(og); int serialno=ogg_page_serialno(og); long pageno=ogg_page_pageno(og); int segments=header[26];
if(ogg_stream_check(os)) return -1;
/* clean up 'returned data' */
{ long lr=os->lacing_returned; long br=os->body_returned;
/* body data */ if(br){
os->body_fill-=br; if(os->body_fill)
memmove(os->body_data,os->body_data+br,os->body_fill);
os->body_returned=0;
}
/* make a note of dropped data in segment table */ if(os->pageno!=-1){
os->lacing_vals[os->lacing_fill++]=0x400;
os->lacing_packet++;
}
}
/* are we a 'continued packet' page? If so, we may need to skip
some segments */ if(continued){ if(os->lacing_fill<1 ||
(os->lacing_vals[os->lacing_fill-1]&0xff)<255 ||
os->lacing_vals[os->lacing_fill-1]==0x400){
bos=0; for(;segptr<segments;segptr++){ int val=header[27+segptr];
body+=val;
bodysize-=val; if(val<255){
segptr++; break;
}
}
}
}
/* 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=os->lacing_returned;
if(os->lacing_packet<=ptr)return(0);
if(os->lacing_vals[ptr]&0x400){ /* we need to tell the codec there's a gap; it might need to
handle previous packet dependencies. */
os->lacing_returned++;
os->packetno++; return(-1);
}
if(!op && !adv)return(1); /* just using peek as an inexpensive way to ask if there's a whole packet
waiting */
/* Gather the whole packet. We'll have no holes or a partial packet */
{ int size=os->lacing_vals[ptr]&0xff; long bytes=size; int eos=os->lacing_vals[ptr]&0x200; /* last packet of the stream? */ int bos=os->lacing_vals[ptr]&0x100; /* first packet of the stream? */
while(size==255){ int val=os->lacing_vals[++ptr];
size=val&0xff; if(val&0x200)eos=0x200;
bytes+=size;
}
/* packet number just follows sequence/gap; adjust the input number
for that */ if(no==0){
sequence=0;
}else{
sequence++; if(no>lastno+1)
sequence++;
}
lastno=no; if(op->packetno!=sequence){
fprintf(stderr,"incorrect packet sequence %ld != %d\n",
(long)(op->packetno),sequence); exit(1);
}
/* Test data */ for(j=0;j<op->bytes;j++) if(op->packet[j]!=((j+no)&0xff)){
fprintf(stderr,"body data mismatch (1) at pos %ld: %x!=%lx!\n\n",
j,op->packet[j],(j+no)&0xff); exit(1);
}
}
void check_page(unsignedchar *data,constint *header,ogg_page *og){ long j; /* Test data */ for(j=0;j<og->body_len;j++) if(og->body[j]!=data[j]){
fprintf(stderr,"body data mismatch (2) at pos %ld: %x!=%x!\n\n",
j,data[j],og->body[j]); exit(1);
}
void test_pack(constint *pl, constint **headers, int byteskip, int pageskip, int packetskip){ unsignedchar *data=_ogg_malloc(1024*1024); /* for scripted test cases only */ long inptr=0; long outptr=0; long deptr=0; long depacket=0; long granule_pos=7,pageno=0; int i,j,packets,pageout=pageskip; int eosflag=0; int bosflag=0;
/* submit it to deconstitution */
ogg_stream_pagein(&os_de,&og_de);
/* packets out? */ while(ogg_stream_packetpeek(&os_de,&op_de2)>0){
ogg_stream_packetpeek(&os_de,NULL);
ogg_stream_packetout(&os_de,&op_de); /* just catching them all */
/* verify peek and out match */ if(compare_packet(&op_de,&op_de2)){
fprintf(stderr,"packetout != packetpeek! pos=%ld\n",
depacket); exit(1);
}
/* verify the packet! */ /* check data */ if(memcmp(data+depacket,op_de.packet,op_de.bytes)){
fprintf(stderr,"packet data mismatch in decode! pos=%ld\n",
depacket); exit(1);
} /* check bos flag */ if(bosflag==0 && op_de.b_o_s==0){
fprintf(stderr,"b_o_s flag not set on packet!\n"); exit(1);
} if(bosflag && op_de.b_o_s){
fprintf(stderr,"b_o_s flag incorrectly set on packet!\n"); exit(1);
}
bosflag=1;
depacket+=op_de.bytes;
/* check eos flag */ if(eosflag){
fprintf(stderr,"Multiple decoded packets with eos flag!\n"); exit(1);
}
if(op_de.e_o_s)eosflag=1;
/* check granulepos flag */ if(op_de.granulepos!=-1){
fprintf(stderr," granule:%ld ",(long)op_de.granulepos);
}
}
}
}
}
}
}
_ogg_free(data); if(headers[pageno]!=NULL){
fprintf(stderr,"did not write last page!\n"); exit(1);
} if(headers[pageout]!=NULL){
fprintf(stderr,"did not decode last page!\n"); exit(1);
} if(inptr!=outptr){
fprintf(stderr,"encoded page data incomplete!\n"); exit(1);
} if(inptr!=deptr){
fprintf(stderr,"decoded page data incomplete!\n"); exit(1);
} if(inptr!=depacket){
fprintf(stderr,"decoded packet data incomplete!\n"); exit(1);
} if(!eosflag){
fprintf(stderr,"Never got a packet with EOS set!\n"); exit(1);
}
fprintf(stderr,"ok.\n");
}
{ /* continuing packet test; with page spill expansion, we have to
overflow the lacing table. */ constint packets[]={0,65500,259,255,-1}; constint *headret[]={head1_4,head2_4,head3_4,NULL};
fprintf(stderr,"testing single packet page span... ");
test_pack(packets,headret,0,0,0);
}
fprintf(stderr,"testing max packet segments... ");
test_pack(packets,headret,0,0,0);
}
{ /* packet that overspans over an entire page */ constint packets[]={0,100,130049,259,255,-1}; constint *headret[]={head1_6,head2_6,head3_6,head4_6,NULL};
fprintf(stderr,"testing very large packets... ");
test_pack(packets,headret,0,0,0);
}
#ifndef DISABLE_CRC
{ /* test for the libogg 1.1.1 resync in large continuation bug
found by Josh Coalson) */ constint packets[]={0,100,130049,259,255,-1}; constint *headret[]={head1_6,head2_6,head3_6,head4_6,NULL};
fprintf(stderr,"testing continuation resync in very large packets... ");
test_pack(packets,headret,100,2,3);
} #else
fprintf(stderr,"Skipping continuation resync test due to --disable-crc\n"); #endif
{ /* term only page. why not? */ constint packets[]={0,100,64770,-1}; constint *headret[]={head1_7,head2_7,head3_7,NULL};
fprintf(stderr,"testing zero data page (1 nil packet)... ");
test_pack(packets,headret,0,0,0);
}
{ /* build a bunch of pages for testing */ unsignedchar *data=_ogg_malloc(1024*1024); int pl[]={0, 1,1,98,4079, 1,1,2954,2057, 76,34,912,0,234,1000,1000, 1000,300,-1}; int inptr=0,i,j;
ogg_page og[5];
ogg_stream_reset(&os_en);
for(i=0;pl[i]!=-1;i++){
ogg_packet op; int len=pl[i];
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.