/************************************************************************ * Copyright (C) 2002-2009, Xiph.org Foundation * Copyright (C) 2010, Robin Watts for Pinknoise Productions Ltd * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the names of the Xiph.org Foundation nor Pinknoise * Productions Ltd nor the names of its contributors may be used to * endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ************************************************************************ function: maintain the info structure, info <-> header packets ************************************************************************/ /* general handling of the header and the vorbis_info structure (and substructures) */ #include #include #include #include "ogg.h" #include "ivorbiscodec.h" #include "codec_internal.h" #include "codebook.h" #include "misc.h" #include "os.h" /* helpers */ static void _v_readstring(oggpack_buffer *o,char *buf,int bytes){ while(bytes--){ *buf++=(char)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;icomments;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;icomments;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;icomments;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){ if(ci->mode_param)_ogg_free(ci->mode_param); if(ci->map_param){ for(i=0;imaps;i++) /* unpack does the range checking */ mapping_clear_info(ci->map_param+i); _ogg_free(ci->map_param); } if(ci->floor_param){ for(i=0;ifloors;i++) /* unpack does the range checking */ if(ci->floor_type[i]) floor1_free_info(ci->floor_param[i]); else floor0_free_info(ci->floor_param[i]); _ogg_free(ci->floor_param); _ogg_free(ci->floor_type); } if(ci->residue_param){ for(i=0;iresidues;i++) /* unpack does the range checking */ res_clear_info(ci->residue_param+i); _ogg_free(ci->residue_param); } if(ci->book_param){ for(i=0;ibooks;i++) vorbis_book_clear(ci->book_param+i); _ogg_free(ci->book_param); } _ogg_free(ci); } memset(vi,0,sizeof(*vi)); } /* Header packing/unpacking ********************************************/ 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<blocksizes[1]=1<rate>=64000 || ci->blocksizes[1]>4096)goto err_out; #else if(vi->rate<64000 && ci->blocksizes[1]>4096)goto err_out; #endif 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]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); } 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;icomments;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 */ 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=(codebook *)_ogg_calloc(ci->books,sizeof(*ci->book_param)); for(i=0;ibooks;i++) if(vorbis_book_unpack(opb,ci->book_param+i))goto err_out; /* time backend settings, not actually used */ i=oggpack_read(opb,6); for(;i>=0;i--) if(oggpack_read(opb,16)!=0)goto err_out; /* floor backend settings */ ci->floors=oggpack_read(opb,6)+1; ci->floor_param=_ogg_malloc(sizeof(*ci->floor_param)*ci->floors); ci->floor_type=_ogg_malloc(sizeof(*ci->floor_type)*ci->floors); for(i=0;ifloors;i++){ ci->floor_type[i]=(char)oggpack_read(opb,16); if(ci->floor_type[i]<0 || ci->floor_type[i]>=VI_FLOORB)goto err_out; if(ci->floor_type[i]) ci->floor_param[i]=floor1_info_unpack(vi,opb); else ci->floor_param[i]=floor0_info_unpack(vi,opb); if(!ci->floor_param[i])goto err_out; } /* residue backend settings */ ci->residues=oggpack_read(opb,6)+1; ci->residue_param=_ogg_malloc(sizeof(*ci->residue_param)*ci->residues); for(i=0;iresidues;i++) if(res_unpack(ci->residue_param+i,vi,opb))goto err_out; /* map backend settings */ ci->maps=oggpack_read(opb,6)+1; ci->map_param=_ogg_malloc(sizeof(*ci->map_param)*ci->maps); for(i=0;imaps;i++){ if(oggpack_read(opb,16)!=0)goto err_out; if(mapping_info_unpack(ci->map_param+i,vi,opb))goto err_out; } /* mode settings */ ci->modes=oggpack_read(opb,6)+1; ci->mode_param= (vorbis_info_mode *)_ogg_malloc(ci->modes*sizeof(*ci->mode_param)); for(i=0;imodes;i++){ ci->mode_param[i].blockflag=(unsigned char)oggpack_read(opb,1); if(oggpack_read(opb,16))goto err_out; if(oggpack_read(opb,16))goto err_out; ci->mode_param[i].mapping=(unsigned char)oggpack_read(opb,8); 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_dsp_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); }