Blame |
Last modification |
View Log
| RSS feed
/********************************************************************
* *
* 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-2003 *
* BY THE Xiph.Org FOUNDATION http://www.xiph.org/ *
* *
********************************************************************
function: maintain the info structure, info <-> header packets
********************************************************************/
/* general handling of the header and the vorbis_info structure (and
substructures) */
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "ogg.h"
#include "ivorbiscodec.h"
#include "codec_internal.h"
#include "codebook.h"
#include "registry.h"
#include "window.h"
#include "misc.h"
#include "os.h"
/* helpers */
static void _v_readstring
(oggpack_buffer
*o
,char *buf
,int bytes
){
while(bytes
--){
*buf
++=oggpack_read
(o
,8);
}
}
void vorbis_comment_init
(vorbis_comment
*vc
){
memset(vc
,0,sizeof(*vc
));
}
/* This is more or less the same as strncasecmp - but that doesn't exist
* everywhere, and this is a fairly trivial function, so we include it */
static int tagcompare
(const char *s1
, const char *s2
, int n
){
int c
=0;
while(c
< n
){
if(toupper(s1
[c
]) != toupper(s2
[c
]))
return !0;
c
++;
}
return 0;
}
char *vorbis_comment_query
(vorbis_comment
*vc
, char *tag
, int count
){
long i
;
int found
= 0;
int taglen
= strlen(tag
)+1; /* +1 for the = we append */
char *fulltag
= (char *)alloca
(taglen
+ 1);
strcpy(fulltag
, tag
);
strcat(fulltag
, "=");
for(i
=0;i
<vc
->comments
;i
++){
if(!tagcompare
(vc
->user_comments
[i
], fulltag
, taglen
)){
if(count
== found
)
/* We return a pointer to the data, not a copy */
return vc
->user_comments
[i
] + taglen
;
else
found
++;
}
}
return NULL
; /* didn't find anything */
}
int vorbis_comment_query_count
(vorbis_comment
*vc
, char *tag
){
int i
,count
=0;
int taglen
= strlen(tag
)+1; /* +1 for the = we append */
char *fulltag
= (char *)alloca
(taglen
+1);
strcpy(fulltag
,tag
);
strcat(fulltag
, "=");
for(i
=0;i
<vc
->comments
;i
++){
if(!tagcompare
(vc
->user_comments
[i
], fulltag
, taglen
))
count
++;
}
return count
;
}
void vorbis_comment_clear
(vorbis_comment
*vc
){
if(vc
){
long i
;
for(i
=0;i
<vc
->comments
;i
++)
if(vc
->user_comments
[i
])_ogg_free
(vc
->user_comments
[i
]);
if(vc
->user_comments
)_ogg_free
(vc
->user_comments
);
if(vc
->comment_lengths
)_ogg_free
(vc
->comment_lengths
);
if(vc
->vendor
)_ogg_free
(vc
->vendor
);
}
memset(vc
,0,sizeof(*vc
));
}
/* blocksize 0 is guaranteed to be short, 1 is guarantted to be long.
They may be equal, but short will never ge greater than long */
int vorbis_info_blocksize
(vorbis_info
*vi
,int zo
){
codec_setup_info
*ci
= (codec_setup_info
*)vi
->codec_setup
;
return ci
? ci
->blocksizes
[zo
] : -1;
}
/* used by synthesis, which has a full, alloced vi */
void vorbis_info_init
(vorbis_info
*vi
){
memset(vi
,0,sizeof(*vi
));
vi
->codec_setup
=(codec_setup_info
*)_ogg_calloc
(1,sizeof(codec_setup_info
));
}
void vorbis_info_clear
(vorbis_info
*vi
){
codec_setup_info
*ci
=(codec_setup_info
*)vi
->codec_setup
;
int i
;
if(ci
){
for(i
=0;i
<ci
->modes
;i
++)
if(ci
->mode_param
[i
])_ogg_free
(ci
->mode_param
[i
]);
for(i
=0;i
<ci
->maps
;i
++) /* unpack does the range checking */
_mapping_P
[ci
->map_type
[i
]]->free_info
(ci
->map_param
[i
]);
for(i
=0;i
<ci
->floors
;i
++) /* unpack does the range checking */
_floor_P
[ci
->floor_type
[i
]]->free_info
(ci
->floor_param
[i
]);
for(i
=0;i
<ci
->residues
;i
++) /* unpack does the range checking */
_residue_P
[ci
->residue_type
[i
]]->free_info
(ci
->residue_param
[i
]);
for(i
=0;i
<ci
->books
;i
++){
if(ci
->book_param
[i
]){
/* knows if the book was not alloced */
vorbis_staticbook_destroy
(ci
->book_param
[i
]);
}
if(ci
->fullbooks
)
vorbis_book_clear
(ci
->fullbooks
+i
);
}
if(ci
->fullbooks
)
_ogg_free
(ci
->fullbooks
);
_ogg_free
(ci
);
}
memset(vi
,0,sizeof(*vi
));
}
/* Header packing/unpacking ********************************************/
static int _vorbis_unpack_info
(vorbis_info
*vi
,oggpack_buffer
*opb
){
codec_setup_info
*ci
=(codec_setup_info
*)vi
->codec_setup
;
if(!ci
)return(OV_EFAULT
);
vi
->version
=oggpack_read
(opb
,32);
if(vi
->version
!=0)return(OV_EVERSION
);
vi
->channels
=oggpack_read
(opb
,8);
vi
->rate
=oggpack_read
(opb
,32);
vi
->bitrate_upper
=oggpack_read
(opb
,32);
vi
->bitrate_nominal
=oggpack_read
(opb
,32);
vi
->bitrate_lower
=oggpack_read
(opb
,32);
ci
->blocksizes
[0]=1<<oggpack_read
(opb
,4);
ci
->blocksizes
[1]=1<<oggpack_read
(opb
,4);
if(vi
->rate
<1)goto err_out
;
if(vi
->channels
<1)goto err_out
;
if(ci
->blocksizes
[0]<64)goto err_out
;
if(ci
->blocksizes
[1]<ci
->blocksizes
[0])goto err_out
;
if(ci
->blocksizes
[1]>8192)goto err_out
;
if(oggpack_read
(opb
,1)!=1)goto err_out
; /* EOP check */
return(0);
err_out
:
vorbis_info_clear
(vi
);
return(OV_EBADHEADER
);
}
static int _vorbis_unpack_comment
(vorbis_comment
*vc
,oggpack_buffer
*opb
){
int i
;
int vendorlen
=oggpack_read
(opb
,32);
if(vendorlen
<0)goto err_out
;
vc
->vendor
=(char *)_ogg_calloc
(vendorlen
+1,1);
_v_readstring
(opb
,vc
->vendor
,vendorlen
);
vc
->comments
=oggpack_read
(opb
,32);
if(vc
->comments
<0)goto err_out
;
vc
->user_comments
=(char **)_ogg_calloc
(vc
->comments
+1,sizeof(*vc
->user_comments
));
vc
->comment_lengths
=(int *)_ogg_calloc
(vc
->comments
+1, sizeof(*vc
->comment_lengths
));
for(i
=0;i
<vc
->comments
;i
++){
int len
=oggpack_read
(opb
,32);
if(len
<0)goto err_out
;
vc
->comment_lengths
[i
]=len
;
vc
->user_comments
[i
]=(char *)_ogg_calloc
(len
+1,1);
_v_readstring
(opb
,vc
->user_comments
[i
],len
);
}
if(oggpack_read
(opb
,1)!=1)goto err_out
; /* EOP check */
return(0);
err_out
:
vorbis_comment_clear
(vc
);
return(OV_EBADHEADER
);
}
/* all of the real encoding details are here. The modes, books,
everything */
static int _vorbis_unpack_books
(vorbis_info
*vi
,oggpack_buffer
*opb
){
codec_setup_info
*ci
=(codec_setup_info
*)vi
->codec_setup
;
int i
;
if(!ci
)return(OV_EFAULT
);
/* codebooks */
ci
->books
=oggpack_read
(opb
,8)+1;
/*ci->book_param=_ogg_calloc(ci->books,sizeof(*ci->book_param));*/
for(i
=0;i
<ci
->books
;i
++){
ci
->book_param
[i
]=(static_codebook
*)_ogg_calloc
(1,sizeof(*ci
->book_param
[i
]));
if(vorbis_staticbook_unpack
(opb
,ci
->book_param
[i
]))goto err_out
;
}
/* time backend settings */
ci
->times
=oggpack_read
(opb
,6)+1;
/*ci->time_type=_ogg_malloc(ci->times*sizeof(*ci->time_type));*/
/*ci->time_param=_ogg_calloc(ci->times,sizeof(void *));*/
for(i
=0;i
<ci
->times
;i
++){
ci
->time_type
[i
]=oggpack_read
(opb
,16);
if(ci
->time_type
[i
]<0 || ci
->time_type
[i
]>=VI_TIMEB
)goto err_out
;
/* ci->time_param[i]=_time_P[ci->time_type[i]]->unpack(vi,opb);
Vorbis I has no time backend */
/*if(!ci->time_param[i])goto err_out;*/
}
/* floor backend settings */
ci
->floors
=oggpack_read
(opb
,6)+1;
/*ci->floor_type=_ogg_malloc(ci->floors*sizeof(*ci->floor_type));*/
/*ci->floor_param=_ogg_calloc(ci->floors,sizeof(void *));*/
for(i
=0;i
<ci
->floors
;i
++){
ci
->floor_type
[i
]=oggpack_read
(opb
,16);
if(ci
->floor_type
[i
]<0 || ci
->floor_type
[i
]>=VI_FLOORB
)goto err_out
;
ci
->floor_param
[i
]=_floor_P
[ci
->floor_type
[i
]]->unpack
(vi
,opb
);
if(!ci
->floor_param
[i
])goto err_out
;
}
/* residue backend settings */
ci
->residues
=oggpack_read
(opb
,6)+1;
/*ci->residue_type=_ogg_malloc(ci->residues*sizeof(*ci->residue_type));*/
/*ci->residue_param=_ogg_calloc(ci->residues,sizeof(void *));*/
for(i
=0;i
<ci
->residues
;i
++){
ci
->residue_type
[i
]=oggpack_read
(opb
,16);
if(ci
->residue_type
[i
]<0 || ci
->residue_type
[i
]>=VI_RESB
)goto err_out
;
ci
->residue_param
[i
]=_residue_P
[ci
->residue_type
[i
]]->unpack
(vi
,opb
);
if(!ci
->residue_param
[i
])goto err_out
;
}
/* map backend settings */
ci
->maps
=oggpack_read
(opb
,6)+1;
/*ci->map_type=_ogg_malloc(ci->maps*sizeof(*ci->map_type));*/
/*ci->map_param=_ogg_calloc(ci->maps,sizeof(void *));*/
for(i
=0;i
<ci
->maps
;i
++){
ci
->map_type
[i
]=oggpack_read
(opb
,16);
if(ci
->map_type
[i
]<0 || ci
->map_type
[i
]>=VI_MAPB
)goto err_out
;
ci
->map_param
[i
]=_mapping_P
[ci
->map_type
[i
]]->unpack
(vi
,opb
);
if(!ci
->map_param
[i
])goto err_out
;
}
/* mode settings */
ci
->modes
=oggpack_read
(opb
,6)+1;
/*vi->mode_param=_ogg_calloc(vi->modes,sizeof(void *));*/
for(i
=0;i
<ci
->modes
;i
++){
ci
->mode_param
[i
]=(vorbis_info_mode
*)_ogg_calloc
(1,sizeof(*ci
->mode_param
[i
]));
ci
->mode_param
[i
]->blockflag
=oggpack_read
(opb
,1);
ci
->mode_param
[i
]->windowtype
=oggpack_read
(opb
,16);
ci
->mode_param
[i
]->transformtype
=oggpack_read
(opb
,16);
ci
->mode_param
[i
]->mapping
=oggpack_read
(opb
,8);
if(ci
->mode_param
[i
]->windowtype
>=VI_WINDOWB
)goto err_out
;
if(ci
->mode_param
[i
]->transformtype
>=VI_WINDOWB
)goto err_out
;
if(ci
->mode_param
[i
]->mapping
>=ci
->maps
)goto err_out
;
}
if(oggpack_read
(opb
,1)!=1)goto err_out
; /* top level EOP check */
return(0);
err_out
:
vorbis_info_clear
(vi
);
return(OV_EBADHEADER
);
}
/* 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
codebook. */
int vorbis_synthesis_headerin
(vorbis_info
*vi
,vorbis_comment
*vc
,ogg_packet
*op
){
oggpack_buffer opb
;
if(op
){
oggpack_readinit
(&opb
,op
->packet
);
/* Which of the three types of header is this? */
/* Also verify header-ness, vorbis */
{
char buffer
[6];
int packtype
=oggpack_read
(&opb
,8);
memset(buffer
,0,6);
_v_readstring
(&opb
,buffer
,6);
if(memcmp(buffer
,"vorbis",6)){
/* not a vorbis header */
return(OV_ENOTVORBIS
);
}
switch(packtype
){
case 0x01: /* least significant *bit* is read first */
if(!op
->b_o_s
){
/* Not the initial packet */
return(OV_EBADHEADER
);
}
if(vi
->rate
!=0){
/* previously initialized info header */
return(OV_EBADHEADER
);
}
return(_vorbis_unpack_info
(vi
,&opb
));
case 0x03: /* least significant *bit* is read first */
if(vi
->rate
==0){
/* um... we didn't get the initial header */
return(OV_EBADHEADER
);
}
return(_vorbis_unpack_comment
(vc
,&opb
));
case 0x05: /* least significant *bit* is read first */
if(vi
->rate
==0 || vc
->vendor
==NULL
){
/* um... we didn;t get the initial header or comments yet */
return(OV_EBADHEADER
);
}
return(_vorbis_unpack_books
(vi
,&opb
));
default:
/* Not a valid vorbis header type */
return(OV_EBADHEADER
);
break;
}
}
}
return(OV_EBADHEADER
);
}