Commit 4ce4ec78 authored by Monty's avatar Monty
Browse files

Merge recent vorbifile changes, extensions and fixes back from

reference 1.2.2 and 1.2.3 into tremor.



git-svn-id: https://svn.xiph.org/trunk/Tremor@16259 0101bb08-14d6-0310-b084-bc0e0c8e3800
parent afdecda7
......@@ -16,13 +16,17 @@ libvorbisidec_la_SOURCES = mdct.c block.c window.c \
asm_arm.h ivorbiscodec.h
libvorbisidec_la_LDFLAGS = -version-info @V_LIB_CURRENT@:@V_LIB_REVISION@:@V_LIB_AGE@
EXTRA_PROGRAMS = ivorbisfile_example
EXTRA_PROGRAMS = ivorbisfile_example iseeking_example
CLEANFILES = $(EXTRA_PROGRAMS) $(lib_LTLIBRARIES)
ivorbisfile_example_SOURCES = ivorbisfile_example.c
ivorbisfile_example_LDFLAGS = -static
ivorbisfile_example_LDADD = libvorbisidec.la
iseeking_example_SOURCES = iseeking_example.c
iseeking_example_LDFLAGS = -static
iseeking_example_LDADD = libvorbisidec.la
includedir = $(prefix)/include/tremor
include_HEADERS = ivorbiscodec.h ivorbisfile.h ogg.h os_types.h config_types.h
......@@ -30,6 +34,7 @@ include_HEADERS = ivorbiscodec.h ivorbisfile.h ogg.h os_types.h config_types.h
example:
-ln -fs . vorbis
$(MAKE) ivorbisfile_example
$(MAKE) iseeking_example
debug:
$(MAKE) all CFLAGS="@DEBUG@"
......
......@@ -293,6 +293,31 @@ static int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *opb){
return(OV_EBADHEADER);
}
/* Is this packet a vorbis ID header? */
int vorbis_synthesis_idheader(ogg_packet *op){
oggpack_buffer opb;
char buffer[6];
if(op){
oggpack_readinit(&opb,op->packet);
if(!op->b_o_s)
return(0); /* Not the initial packet */
if(oggpack_read(&opb,8) != 1)
return 0; /* not an ID header */
memset(buffer,0,6);
_v_readstring(&opb,buffer,6);
if(memcmp(buffer,"vorbis",6))
return 0; /* not vorbis */
return 1;
}
return 0;
}
/* The Vorbis header is in three packets; the initial small packet in
the first page that identifies basic parameters, a second packet
with bitstream comments and a third packet that holds the
......
/********************************************************************
* *
* THIS FILE IS PART OF THE OggVorbis 'TREMOR' 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 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2009 *
* BY THE Xiph.Org FOUNDATION http://www.xiph.org/ *
* *
********************************************************************
function: illustrate seeking, and test it too
last mod: $Id: iseeking_example.c 16037 2009-05-26 21:10:58Z xiphmont $
********************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <vorbis/ivorbiscodec.h>
#include <vorbis/ivorbisfile.h>
#ifdef _WIN32 /* We need the following two to set stdin/stdout to binary */
# include <io.h>
# include <fcntl.h>
#endif
void _verify(OggVorbis_File *ov,
ogg_int64_t val,
ogg_int64_t pcmval,
ogg_int64_t timeval,
ogg_int64_t pcmlength,
char *bigassbuffer){
int j;
long bread;
char buffer[4096];
int dummy;
ogg_int64_t pos;
/* verify the raw position, the pcm position and position decode */
if(val!=-1 && ov_raw_tell(ov)<val){
fprintf(stderr,"raw position out of tolerance: requested %ld, got %ld\n",
(long)val,(long)ov_raw_tell(ov));
exit(1);
}
if(pcmval!=-1 && ov_pcm_tell(ov)>pcmval){
fprintf(stderr,"pcm position out of tolerance: requested %ld, got %ld\n",
(long)pcmval,(long)ov_pcm_tell(ov));
exit(1);
}
if(timeval!=-1 && ov_time_tell(ov)>timeval){
fprintf(stderr,"time position out of tolerance: requested %ld, got %ld\n",
(long)timeval,(long)ov_time_tell(ov));
exit(1);
}
pos=ov_pcm_tell(ov);
if(pos<0 || pos>pcmlength){
fprintf(stderr,"pcm position out of bounds: got %ld\n",(long)pos);
exit(1);
}
bread=ov_read(ov,buffer,4096,&dummy);
for(j=0;j<bread;j++){
if(buffer[j]!=bigassbuffer[j+pos*4]){
fprintf(stderr,"data position after seek doesn't match pcm position\n");
{
FILE *f=fopen("a.m","w");
for(j=0;j<bread;j++)fprintf(f,"%d\n",(int)buffer[j]);
fclose(f);
f=fopen("b.m","w");
for(j=0;j<bread;j++)fprintf(f,"%d\n",(int)bigassbuffer[j+pos*2]);
fclose(f);
}
exit(1);
}
}
}
int main(){
OggVorbis_File ov;
int i,ret;
ogg_int64_t pcmlength;
ogg_int64_t timelength;
char *bigassbuffer;
int dummy;
#ifdef _WIN32 /* We need to set stdin/stdout to binary mode. Damn windows. */
_setmode( _fileno( stdin ), _O_BINARY );
#endif
/* open the file/pipe on stdin */
if(ov_open(stdin, &ov, NULL, 0) < 0) {
fprintf(stderr,"Could not open input as an OggVorbis file.\n\n");
exit(1);
}
if(ov_seekable(&ov)){
/* to simplify our own lives, we want to assume the whole file is
stereo. Verify this to avoid potentially mystifying users
(pissing them off is OK, just don't confuse them) */
for(i=0;i<ov.links;i++){
vorbis_info *vi=ov_info(&ov,i);
if(vi->channels!=2){
fprintf(stderr,"Sorry; right now seeking_test can only use Vorbis files\n"
"that are entirely stereo.\n\n");
exit(1);
}
}
/* because we want to do sample-level verification that the seek
does what it claimed, decode the entire file into memory */
pcmlength=ov_pcm_total(&ov,-1);
timelength=ov_time_total(&ov,-1);
bigassbuffer=malloc(pcmlength*4); /* w00t */
i=0;
while(i<pcmlength*4){
int ret=ov_read(&ov,bigassbuffer+i,pcmlength*4-i,&dummy);
if(ret<0)continue;
if(ret){
i+=ret;
}else{
pcmlength=i/4;
}
fprintf(stderr,"\rloading.... [%ld left] ",
(long)(pcmlength*4-i));
}
{
ogg_int64_t length=ov.end;
fprintf(stderr,"\rtesting raw seeking to random places in %ld bytes....\n",
(long)length);
for(i=0;i<1000;i++){
ogg_int64_t val=rand()*length/RAND_MAX;
fprintf(stderr,"\r\t%d [raw position %ld]... ",i,(long)val);
ret=ov_raw_seek(&ov,val);
if(ret<0){
fprintf(stderr,"seek failed: %d\n",ret);
exit(1);
}
_verify(&ov,val,-1,-1.,pcmlength,bigassbuffer);
}
}
fprintf(stderr,"\r");
{
fprintf(stderr,"testing pcm page seeking to random places in %ld samples....\n",
(long)pcmlength);
for(i=0;i<1000;i++){
ogg_int64_t val=rand()*pcmlength/RAND_MAX;
fprintf(stderr,"\r\t%d [pcm position %ld]... ",i,(long)val);
ret=ov_pcm_seek_page(&ov,val);
if(ret<0){
fprintf(stderr,"seek failed: %d\n",ret);
exit(1);
}
_verify(&ov,-1,val,-1.,pcmlength,bigassbuffer);
}
}
fprintf(stderr,"\r");
{
fprintf(stderr,"testing pcm exact seeking to random places in %ld samples....\n",
(long)pcmlength);
for(i=0;i<1000;i++){
ogg_int64_t val=rand()*pcmlength/RAND_MAX;
fprintf(stderr,"\r\t%d [pcm position %ld]... ",i,(long)val);
ret=ov_pcm_seek(&ov,val);
if(ret<0){
fprintf(stderr,"seek failed: %d\n",ret);
exit(1);
}
if(ov_pcm_tell(&ov)!=val){
fprintf(stderr,"Declared position didn't perfectly match request: %ld != %ld\n",
(long)val,(long)ov_pcm_tell(&ov));
exit(1);
}
_verify(&ov,-1,val,-1.,pcmlength,bigassbuffer);
}
}
fprintf(stderr,"\r");
{
fprintf(stderr,"testing time page seeking to random places in %ld seconds....\n",
(long)timelength);
for(i=0;i<1000;i++){
ogg_int64_t val=rand()*timelength/RAND_MAX;
fprintf(stderr,"\r\t%d [time position %ld]... ",i,(long)val);
ret=ov_time_seek_page(&ov,val);
if(ret<0){
fprintf(stderr,"seek failed: %d\n",ret);
exit(1);
}
_verify(&ov,-1,-1,val,pcmlength,bigassbuffer);
}
}
fprintf(stderr,"\r");
{
fprintf(stderr,"testing time exact seeking to random places in %ld seconds....\n",
(long)timelength);
for(i=0;i<1000;i++){
ogg_int64_t val=rand()*timelength/RAND_MAX;
fprintf(stderr,"\r\t%d [time position %ld]... ",i,(long)val);
ret=ov_time_seek(&ov,val);
if(ret<0){
fprintf(stderr,"seek failed: %d\n",ret);
exit(1);
}
if(ov_time_tell(&ov)<val-1 || ov_time_tell(&ov)>val+1){
fprintf(stderr,"Declared position didn't perfectly match request: %ld != %ld\n",
(long)val,(long)ov_time_tell(&ov));
exit(1);
}
_verify(&ov,-1,-1,val,pcmlength,bigassbuffer);
}
}
fprintf(stderr,"\r \nOK.\n\n");
}else{
fprintf(stderr,"Standard input was not seekable.\n");
}
ov_clear(&ov);
return 0;
}
......@@ -165,6 +165,7 @@ extern int vorbis_block_clear(vorbis_block *vb);
extern void vorbis_dsp_clear(vorbis_dsp_state *v);
/* Vorbis PRIMITIVES: synthesis layer *******************************/
extern int vorbis_synthesis_idheader(ogg_packet *op);
extern int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc,
ogg_packet *op);
......
......@@ -6,7 +6,7 @@
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
* THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2003 *
* THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2009 *
* BY THE Xiph.Org FOUNDATION http://www.xiph.org/ *
* *
********************************************************************
......@@ -71,15 +71,18 @@ static long _get_data(OggVorbis_File *vf){
}
/* save a tiny smidge of verbosity to make the code more readable */
static void _seek_helper(OggVorbis_File *vf,ogg_int64_t offset){
if(vf->datasource){
(vf->callbacks.seek_func)(vf->datasource, offset, SEEK_SET);
static int _seek_helper(OggVorbis_File *vf,ogg_int64_t offset){
if(vf->datasource){
if(!(vf->callbacks.seek_func)||
(vf->callbacks.seek_func)(vf->datasource, offset, SEEK_SET) == -1)
return OV_EREAD;
vf->offset=offset;
ogg_sync_reset(vf->oy);
}else{
/* shouldn't happen unless someone writes a broken callback */
return;
return OV_EFAULT;
}
return 0;
}
/* The read/seek functions track absolute position within the stream */
......@@ -146,7 +149,10 @@ static ogg_int64_t _get_prev_page(OggVorbis_File *vf,ogg_page *og){
begin-=CHUNKSIZE;
if(begin<0)
begin=0;
_seek_helper(vf,begin);
ret=_seek_helper(vf,begin);
if(ret)return(ret);
while(vf->offset<end){
ret=_get_next_page(vf,og,end-vf->offset);
if(ret==OV_EREAD)return(OV_EREAD);
......@@ -158,74 +164,114 @@ static ogg_int64_t _get_prev_page(OggVorbis_File *vf,ogg_page *og){
}
}
/* we have the offset. Actually snork and hold the page now */
_seek_helper(vf,offset);
ret=_get_next_page(vf,og,CHUNKSIZE);
if(ret<0)
/* this shouldn't be possible */
return(OV_EFAULT);
/* In a fully compliant, non-multiplexed stream, we'll still be
holding the last page. In multiplexed (or noncompliant streams),
we will probably have to re-read the last page we saw */
if(og->header_len==0){
ogg_page_release(og);
ret=_seek_helper(vf,offset);
if(ret)return(ret);
ret=_get_next_page(vf,og,CHUNKSIZE);
if(ret<0)
/* this shouldn't be possible */
return(OV_EFAULT);
}
return(offset);
}
/* finds each bitstream link one at a time using a bisection search
(has to begin by knowing the offset of the lb's initial page).
Recurses for each link so it can alloc the link storage after
finding them all, then unroll and fill the cache at the same time */
static int _bisect_forward_serialno(OggVorbis_File *vf,
ogg_int64_t begin,
ogg_int64_t searched,
ogg_int64_t end,
ogg_uint32_t currentno,
long m){
ogg_int64_t endsearched=end;
ogg_int64_t next=end;
static void _add_serialno(ogg_page *og,ogg_uint32_t **serialno_list, int *n){
long s = ogg_page_serialno(og);
(*n)++;
if(*serialno_list){
*serialno_list = _ogg_realloc(*serialno_list, sizeof(**serialno_list)*(*n));
}else{
*serialno_list = _ogg_malloc(sizeof(**serialno_list));
}
(*serialno_list)[(*n)-1] = s;
}
/* returns nonzero if found */
static int _lookup_serialno(long s, ogg_uint32_t *serialno_list, int n){
if(serialno_list){
while(n--){
if(*serialno_list == s) return 1;
serialno_list++;
}
}
return 0;
}
static int _lookup_page_serialno(ogg_page *og, ogg_uint32_t *serialno_list, int n){
long s = ogg_page_serialno(og);
return _lookup_serialno(s,serialno_list,n);
}
/* performs the same search as _get_prev_page, but prefers pages of
the specified serial number. If a page of the specified serialno is
spotted during the seek-back-and-read-forward, it will return the
info of last page of the matching serial number instead of the very
last page. If no page of the specified serialno is seen, it will
return the info of last page and alter *serialno. */
static ogg_int64_t _get_prev_page_serial(OggVorbis_File *vf,
ogg_uint32_t *serial_list, int serial_n,
int *serialno, ogg_int64_t *granpos){
ogg_page og={0,0,0,0};
ogg_int64_t begin=vf->offset;
ogg_int64_t end=begin;
ogg_int64_t ret;
/* the below guards against garbage seperating the last and
first pages of two links. */
while(searched<endsearched){
ogg_int64_t bisect;
if(endsearched-searched<CHUNKSIZE){
bisect=searched;
}else{
bisect=(searched+endsearched)/2;
}
_seek_helper(vf,bisect);
ret=_get_next_page(vf,&og,-1);
if(ret==OV_EREAD)return(OV_EREAD);
if(ret<0 || ogg_page_serialno(&og)!=currentno){
endsearched=bisect;
if(ret>=0)next=ret;
}else{
searched=ret+og.header_len+og.body_len;
ogg_int64_t prefoffset=-1;
ogg_int64_t offset=-1;
ogg_uint32_t ret_serialno=-1;
ogg_int64_t ret_gran=-1;
while(offset==-1){
begin-=CHUNKSIZE;
if(begin<0)
begin=0;
ret=_seek_helper(vf,begin);
if(ret)return(ret);
while(vf->offset<end){
ret=_get_next_page(vf,&og,end-vf->offset);
if(ret==OV_EREAD)return(OV_EREAD);
if(ret<0){
ogg_page_release(&og);
break;
}else{
ret_serialno=ogg_page_serialno(&og);
ret_gran=ogg_page_granulepos(&og);
offset=ret;
ogg_page_release(&og);
if(ret_serialno == *serialno){
prefoffset=ret;
*granpos=ret_gran;
}
if(!_lookup_serialno(ret_serialno,serial_list,serial_n)){
/* we fell off the end of the link, which means we seeked
back too far and shouldn't have been looking in that link
to begin with. If we found the preferred serial number,
forget that we saw it. */
prefoffset=-1;
}
}
}
ogg_page_release(&og);
}
_seek_helper(vf,next);
ret=_get_next_page(vf,&og,-1);
if(ret==OV_EREAD)return(OV_EREAD);
if(searched>=end || ret<0){
ogg_page_release(&og);
vf->links=m+1;
vf->offsets=_ogg_malloc((vf->links+1)*sizeof(*vf->offsets));
vf->serialnos=_ogg_malloc(vf->links*sizeof(*vf->serialnos));
vf->offsets[m+1]=searched;
}else{
ret=_bisect_forward_serialno(vf,next,vf->offset,
end,ogg_page_serialno(&og),m+1);
ogg_page_release(&og);
if(ret==OV_EREAD)return(OV_EREAD);
}
vf->offsets[m]=begin;
vf->serialnos[m]=currentno;
return(0);
/* we're not interested in the page... just the serialno and granpos. */
if(prefoffset>=0)return(prefoffset);
*serialno = ret_serialno;
*granpos = ret_gran;
return(offset);
}
/* uses the local ogg_stream storage in vf; this is important for
......@@ -235,11 +281,13 @@ static int _bisect_forward_serialno(OggVorbis_File *vf,
static int _fetch_headers(OggVorbis_File *vf,
vorbis_info *vi,
vorbis_comment *vc,
ogg_uint32_t *serialno,
ogg_uint32_t **serialno_list,
int *serialno_n,
ogg_page *og_ptr){
ogg_page og={0,0,0,0};
ogg_packet op={0,0,0,0,0,0};
int i,ret;
int allbos=0;
if(!og_ptr){
ogg_int64_t llret=_get_next_page(vf,&og,CHUNKSIZE);
......@@ -248,41 +296,121 @@ static int _fetch_headers(OggVorbis_File *vf,
og_ptr=&og;
}
ogg_stream_reset_serialno(vf->os,ogg_page_serialno(og_ptr));
if(serialno)*serialno=vf->os->serialno;
vf->ready_state=STREAMSET;
/* extract the initial header from the first page and verify that the
Ogg bitstream is in fact Vorbis data */
vorbis_info_init(vi);
vorbis_comment_init(vc);
i=0;
while(i<3){
ogg_stream_pagein(vf->os,og_ptr);
while(i<3){
int result=ogg_stream_packetout(vf->os,&op);
if(result==0)break;
if(result==-1){
ret=OV_EBADHEADER;
goto bail_header;
vf->ready_state=OPENED;
/* extract the serialnos of all BOS pages + the first set of vorbis
headers we see in the link */
while(ogg_page_bos(og_ptr)){
if(serialno_list){
if(_lookup_page_serialno(og_ptr,*serialno_list,*serialno_n)){
/* a dupe serialnumber in an initial header packet set == invalid stream */
if(*serialno_list)_ogg_free(*serialno_list);
*serialno_list=0;
*serialno_n=0;
ret=OV_EBADHEADER;
goto bail_header;
}
if((ret=vorbis_synthesis_headerin(vi,vc,&op))){
goto bail_header;
_add_serialno(og_ptr,serialno_list,serialno_n);
}
if(vf->ready_state<STREAMSET){
/* we don't have a vorbis stream in this link yet, so begin
prospective stream setup. We need a stream to get packets */
ogg_stream_reset_serialno(vf->os,ogg_page_serialno(og_ptr));
ogg_stream_pagein(vf->os,og_ptr);
if(ogg_stream_packetout(vf->os,&op) > 0 &&
vorbis_synthesis_idheader(&op)){
/* vorbis header; continue setup */
vf->ready_state=STREAMSET;
if((ret=vorbis_synthesis_headerin(vi,vc,&op))){
ret=OV_EBADHEADER;
goto bail_header;
}
}
i++;
}
if(i<3)
if(_get_next_page(vf,og_ptr,CHUNKSIZE)<0){
ret=OV_EBADHEADER;</