annotate src/opusfile-0.9/src/info.c @ 79:91c729825bca pa_catalina

Update build for AUDIO_COMPONENT_FIX
author Chris Cannam
date Wed, 30 Oct 2019 12:40:34 +0000
parents 7aeed7906520
children
rev   line source
Chris@69 1 /********************************************************************
Chris@69 2 * *
Chris@69 3 * THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. *
Chris@69 4 * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
Chris@69 5 * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
Chris@69 6 * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
Chris@69 7 * *
Chris@69 8 * THE libopusfile SOURCE CODE IS (C) COPYRIGHT 2012 *
Chris@69 9 * by the Xiph.Org Foundation and contributors http://www.xiph.org/ *
Chris@69 10 * *
Chris@69 11 ********************************************************************/
Chris@69 12 #ifdef HAVE_CONFIG_H
Chris@69 13 #include "config.h"
Chris@69 14 #endif
Chris@69 15
Chris@69 16 #include "internal.h"
Chris@69 17 #include <limits.h>
Chris@69 18 #include <string.h>
Chris@69 19
Chris@69 20 static unsigned op_parse_uint16le(const unsigned char *_data){
Chris@69 21 return _data[0]|_data[1]<<8;
Chris@69 22 }
Chris@69 23
Chris@69 24 static int op_parse_int16le(const unsigned char *_data){
Chris@69 25 int ret;
Chris@69 26 ret=_data[0]|_data[1]<<8;
Chris@69 27 return (ret^0x8000)-0x8000;
Chris@69 28 }
Chris@69 29
Chris@69 30 static opus_uint32 op_parse_uint32le(const unsigned char *_data){
Chris@69 31 return _data[0]|(opus_uint32)_data[1]<<8|
Chris@69 32 (opus_uint32)_data[2]<<16|(opus_uint32)_data[3]<<24;
Chris@69 33 }
Chris@69 34
Chris@69 35 static opus_uint32 op_parse_uint32be(const unsigned char *_data){
Chris@69 36 return _data[3]|(opus_uint32)_data[2]<<8|
Chris@69 37 (opus_uint32)_data[1]<<16|(opus_uint32)_data[0]<<24;
Chris@69 38 }
Chris@69 39
Chris@69 40 int opus_head_parse(OpusHead *_head,const unsigned char *_data,size_t _len){
Chris@69 41 OpusHead head;
Chris@69 42 if(_len<8)return OP_ENOTFORMAT;
Chris@69 43 if(memcmp(_data,"OpusHead",8)!=0)return OP_ENOTFORMAT;
Chris@69 44 if(_len<9)return OP_EBADHEADER;
Chris@69 45 head.version=_data[8];
Chris@69 46 if(head.version>15)return OP_EVERSION;
Chris@69 47 if(_len<19)return OP_EBADHEADER;
Chris@69 48 head.channel_count=_data[9];
Chris@69 49 head.pre_skip=op_parse_uint16le(_data+10);
Chris@69 50 head.input_sample_rate=op_parse_uint32le(_data+12);
Chris@69 51 head.output_gain=op_parse_int16le(_data+16);
Chris@69 52 head.mapping_family=_data[18];
Chris@69 53 if(head.mapping_family==0){
Chris@69 54 if(head.channel_count<1||head.channel_count>2)return OP_EBADHEADER;
Chris@69 55 if(head.version<=1&&_len>19)return OP_EBADHEADER;
Chris@69 56 head.stream_count=1;
Chris@69 57 head.coupled_count=head.channel_count-1;
Chris@69 58 if(_head!=NULL){
Chris@69 59 _head->mapping[0]=0;
Chris@69 60 _head->mapping[1]=1;
Chris@69 61 }
Chris@69 62 }
Chris@69 63 else if(head.mapping_family==1){
Chris@69 64 size_t size;
Chris@69 65 int ci;
Chris@69 66 if(head.channel_count<1||head.channel_count>8)return OP_EBADHEADER;
Chris@69 67 size=21+head.channel_count;
Chris@69 68 if(_len<size||head.version<=1&&_len>size)return OP_EBADHEADER;
Chris@69 69 head.stream_count=_data[19];
Chris@69 70 if(head.stream_count<1)return OP_EBADHEADER;
Chris@69 71 head.coupled_count=_data[20];
Chris@69 72 if(head.coupled_count>head.stream_count)return OP_EBADHEADER;
Chris@69 73 for(ci=0;ci<head.channel_count;ci++){
Chris@69 74 if(_data[21+ci]>=head.stream_count+head.coupled_count
Chris@69 75 &&_data[21+ci]!=255){
Chris@69 76 return OP_EBADHEADER;
Chris@69 77 }
Chris@69 78 }
Chris@69 79 if(_head!=NULL)memcpy(_head->mapping,_data+21,head.channel_count);
Chris@69 80 }
Chris@69 81 /*General purpose players should not attempt to play back content with
Chris@69 82 channel mapping family 255.*/
Chris@69 83 else if(head.mapping_family==255)return OP_EIMPL;
Chris@69 84 /*No other channel mapping families are currently defined.*/
Chris@69 85 else return OP_EBADHEADER;
Chris@69 86 if(_head!=NULL)memcpy(_head,&head,head.mapping-(unsigned char *)&head);
Chris@69 87 return 0;
Chris@69 88 }
Chris@69 89
Chris@69 90 void opus_tags_init(OpusTags *_tags){
Chris@69 91 memset(_tags,0,sizeof(*_tags));
Chris@69 92 }
Chris@69 93
Chris@69 94 void opus_tags_clear(OpusTags *_tags){
Chris@69 95 int ncomments;
Chris@69 96 int ci;
Chris@69 97 ncomments=_tags->comments;
Chris@69 98 if(_tags->user_comments!=NULL)ncomments++;
Chris@69 99 for(ci=ncomments;ci-->0;)_ogg_free(_tags->user_comments[ci]);
Chris@69 100 _ogg_free(_tags->user_comments);
Chris@69 101 _ogg_free(_tags->comment_lengths);
Chris@69 102 _ogg_free(_tags->vendor);
Chris@69 103 }
Chris@69 104
Chris@69 105 /*Ensure there's room for up to _ncomments comments.*/
Chris@69 106 static int op_tags_ensure_capacity(OpusTags *_tags,size_t _ncomments){
Chris@69 107 char **user_comments;
Chris@69 108 int *comment_lengths;
Chris@69 109 int cur_ncomments;
Chris@69 110 size_t size;
Chris@69 111 if(OP_UNLIKELY(_ncomments>=(size_t)INT_MAX))return OP_EFAULT;
Chris@69 112 size=sizeof(*_tags->comment_lengths)*(_ncomments+1);
Chris@69 113 if(size/sizeof(*_tags->comment_lengths)!=_ncomments+1)return OP_EFAULT;
Chris@69 114 cur_ncomments=_tags->comments;
Chris@69 115 /*We only support growing.
Chris@69 116 Trimming requires cleaning up the allocated strings in the old space, and
Chris@69 117 is best handled separately if it's ever needed.*/
Chris@69 118 OP_ASSERT(_ncomments>=(size_t)cur_ncomments);
Chris@69 119 comment_lengths=_tags->comment_lengths;
Chris@69 120 comment_lengths=(int *)_ogg_realloc(_tags->comment_lengths,size);
Chris@69 121 if(OP_UNLIKELY(comment_lengths==NULL))return OP_EFAULT;
Chris@69 122 if(_tags->comment_lengths==NULL){
Chris@69 123 OP_ASSERT(cur_ncomments==0);
Chris@69 124 comment_lengths[cur_ncomments]=0;
Chris@69 125 }
Chris@69 126 comment_lengths[_ncomments]=comment_lengths[cur_ncomments];
Chris@69 127 _tags->comment_lengths=comment_lengths;
Chris@69 128 size=sizeof(*_tags->user_comments)*(_ncomments+1);
Chris@69 129 if(size/sizeof(*_tags->user_comments)!=_ncomments+1)return OP_EFAULT;
Chris@69 130 user_comments=(char **)_ogg_realloc(_tags->user_comments,size);
Chris@69 131 if(OP_UNLIKELY(user_comments==NULL))return OP_EFAULT;
Chris@69 132 if(_tags->user_comments==NULL){
Chris@69 133 OP_ASSERT(cur_ncomments==0);
Chris@69 134 user_comments[cur_ncomments]=NULL;
Chris@69 135 }
Chris@69 136 user_comments[_ncomments]=user_comments[cur_ncomments];
Chris@69 137 _tags->user_comments=user_comments;
Chris@69 138 return 0;
Chris@69 139 }
Chris@69 140
Chris@69 141 /*Duplicate a (possibly non-NUL terminated) string with a known length.*/
Chris@69 142 static char *op_strdup_with_len(const char *_s,size_t _len){
Chris@69 143 size_t size;
Chris@69 144 char *ret;
Chris@69 145 size=sizeof(*ret)*(_len+1);
Chris@69 146 if(OP_UNLIKELY(size<_len))return NULL;
Chris@69 147 ret=(char *)_ogg_malloc(size);
Chris@69 148 if(OP_LIKELY(ret!=NULL)){
Chris@69 149 ret=(char *)memcpy(ret,_s,sizeof(*ret)*_len);
Chris@69 150 ret[_len]='\0';
Chris@69 151 }
Chris@69 152 return ret;
Chris@69 153 }
Chris@69 154
Chris@69 155 /*The actual implementation of opus_tags_parse().
Chris@69 156 Unlike the public API, this function requires _tags to already be
Chris@69 157 initialized, modifies its contents before success is guaranteed, and assumes
Chris@69 158 the caller will clear it on error.*/
Chris@69 159 static int opus_tags_parse_impl(OpusTags *_tags,
Chris@69 160 const unsigned char *_data,size_t _len){
Chris@69 161 opus_uint32 count;
Chris@69 162 size_t len;
Chris@69 163 int ncomments;
Chris@69 164 int ci;
Chris@69 165 len=_len;
Chris@69 166 if(len<8)return OP_ENOTFORMAT;
Chris@69 167 if(memcmp(_data,"OpusTags",8)!=0)return OP_ENOTFORMAT;
Chris@69 168 if(len<16)return OP_EBADHEADER;
Chris@69 169 _data+=8;
Chris@69 170 len-=8;
Chris@69 171 count=op_parse_uint32le(_data);
Chris@69 172 _data+=4;
Chris@69 173 len-=4;
Chris@69 174 if(count>len)return OP_EBADHEADER;
Chris@69 175 if(_tags!=NULL){
Chris@69 176 _tags->vendor=op_strdup_with_len((char *)_data,count);
Chris@69 177 if(_tags->vendor==NULL)return OP_EFAULT;
Chris@69 178 }
Chris@69 179 _data+=count;
Chris@69 180 len-=count;
Chris@69 181 if(len<4)return OP_EBADHEADER;
Chris@69 182 count=op_parse_uint32le(_data);
Chris@69 183 _data+=4;
Chris@69 184 len-=4;
Chris@69 185 /*Check to make sure there's minimally sufficient data left in the packet.*/
Chris@69 186 if(count>len>>2)return OP_EBADHEADER;
Chris@69 187 /*Check for overflow (the API limits this to an int).*/
Chris@69 188 if(count>(opus_uint32)INT_MAX-1)return OP_EFAULT;
Chris@69 189 if(_tags!=NULL){
Chris@69 190 int ret;
Chris@69 191 ret=op_tags_ensure_capacity(_tags,count);
Chris@69 192 if(ret<0)return ret;
Chris@69 193 }
Chris@69 194 ncomments=(int)count;
Chris@69 195 for(ci=0;ci<ncomments;ci++){
Chris@69 196 /*Check to make sure there's minimally sufficient data left in the packet.*/
Chris@69 197 if((size_t)(ncomments-ci)>len>>2)return OP_EBADHEADER;
Chris@69 198 count=op_parse_uint32le(_data);
Chris@69 199 _data+=4;
Chris@69 200 len-=4;
Chris@69 201 if(count>len)return OP_EBADHEADER;
Chris@69 202 /*Check for overflow (the API limits this to an int).*/
Chris@69 203 if(count>(opus_uint32)INT_MAX)return OP_EFAULT;
Chris@69 204 if(_tags!=NULL){
Chris@69 205 _tags->user_comments[ci]=op_strdup_with_len((char *)_data,count);
Chris@69 206 if(_tags->user_comments[ci]==NULL)return OP_EFAULT;
Chris@69 207 _tags->comment_lengths[ci]=(int)count;
Chris@69 208 _tags->comments=ci+1;
Chris@69 209 /*Needed by opus_tags_clear() if we fail before parsing the (optional)
Chris@69 210 binary metadata.*/
Chris@69 211 _tags->user_comments[ci+1]=NULL;
Chris@69 212 }
Chris@69 213 _data+=count;
Chris@69 214 len-=count;
Chris@69 215 }
Chris@69 216 if(len>0&&(_data[0]&1)){
Chris@69 217 if(len>(opus_uint32)INT_MAX)return OP_EFAULT;
Chris@69 218 if(_tags!=NULL){
Chris@69 219 _tags->user_comments[ncomments]=(char *)_ogg_malloc(len);
Chris@69 220 if(OP_UNLIKELY(_tags->user_comments[ncomments]==NULL))return OP_EFAULT;
Chris@69 221 memcpy(_tags->user_comments[ncomments],_data,len);
Chris@69 222 _tags->comment_lengths[ncomments]=(int)len;
Chris@69 223 }
Chris@69 224 }
Chris@69 225 return 0;
Chris@69 226 }
Chris@69 227
Chris@69 228 int opus_tags_parse(OpusTags *_tags,const unsigned char *_data,size_t _len){
Chris@69 229 if(_tags!=NULL){
Chris@69 230 OpusTags tags;
Chris@69 231 int ret;
Chris@69 232 opus_tags_init(&tags);
Chris@69 233 ret=opus_tags_parse_impl(&tags,_data,_len);
Chris@69 234 if(ret<0)opus_tags_clear(&tags);
Chris@69 235 else *_tags=*&tags;
Chris@69 236 return ret;
Chris@69 237 }
Chris@69 238 else return opus_tags_parse_impl(NULL,_data,_len);
Chris@69 239 }
Chris@69 240
Chris@69 241 /*The actual implementation of opus_tags_copy().
Chris@69 242 Unlike the public API, this function requires _dst to already be
Chris@69 243 initialized, modifies its contents before success is guaranteed, and assumes
Chris@69 244 the caller will clear it on error.*/
Chris@69 245 static int opus_tags_copy_impl(OpusTags *_dst,const OpusTags *_src){
Chris@69 246 char *vendor;
Chris@69 247 int ncomments;
Chris@69 248 int ret;
Chris@69 249 int ci;
Chris@69 250 vendor=_src->vendor;
Chris@69 251 _dst->vendor=op_strdup_with_len(vendor,strlen(vendor));
Chris@69 252 if(OP_UNLIKELY(_dst->vendor==NULL))return OP_EFAULT;
Chris@69 253 ncomments=_src->comments;
Chris@69 254 ret=op_tags_ensure_capacity(_dst,ncomments);
Chris@69 255 if(OP_UNLIKELY(ret<0))return ret;
Chris@69 256 for(ci=0;ci<ncomments;ci++){
Chris@69 257 int len;
Chris@69 258 len=_src->comment_lengths[ci];
Chris@69 259 OP_ASSERT(len>=0);
Chris@69 260 _dst->user_comments[ci]=op_strdup_with_len(_src->user_comments[ci],len);
Chris@69 261 if(OP_UNLIKELY(_dst->user_comments[ci]==NULL))return OP_EFAULT;
Chris@69 262 _dst->comment_lengths[ci]=len;
Chris@69 263 _dst->comments=ci+1;
Chris@69 264 }
Chris@69 265 if(_src->comment_lengths!=NULL){
Chris@69 266 int len;
Chris@69 267 len=_src->comment_lengths[ncomments];
Chris@69 268 if(len>0){
Chris@69 269 _dst->user_comments[ncomments]=(char *)_ogg_malloc(len);
Chris@69 270 if(OP_UNLIKELY(_dst->user_comments[ncomments]==NULL))return OP_EFAULT;
Chris@69 271 memcpy(_dst->user_comments[ncomments],_src->user_comments[ncomments],len);
Chris@69 272 _dst->comment_lengths[ncomments]=len;
Chris@69 273 }
Chris@69 274 }
Chris@69 275 return 0;
Chris@69 276 }
Chris@69 277
Chris@69 278 int opus_tags_copy(OpusTags *_dst,const OpusTags *_src){
Chris@69 279 OpusTags dst;
Chris@69 280 int ret;
Chris@69 281 opus_tags_init(&dst);
Chris@69 282 ret=opus_tags_copy_impl(&dst,_src);
Chris@69 283 if(OP_UNLIKELY(ret<0))opus_tags_clear(&dst);
Chris@69 284 else *_dst=*&dst;
Chris@69 285 return 0;
Chris@69 286 }
Chris@69 287
Chris@69 288 int opus_tags_add(OpusTags *_tags,const char *_tag,const char *_value){
Chris@69 289 char *comment;
Chris@69 290 size_t tag_len;
Chris@69 291 size_t value_len;
Chris@69 292 int ncomments;
Chris@69 293 int ret;
Chris@69 294 ncomments=_tags->comments;
Chris@69 295 ret=op_tags_ensure_capacity(_tags,ncomments+1);
Chris@69 296 if(OP_UNLIKELY(ret<0))return ret;
Chris@69 297 tag_len=strlen(_tag);
Chris@69 298 value_len=strlen(_value);
Chris@69 299 /*+2 for '=' and '\0'.*/
Chris@69 300 if(tag_len+value_len<tag_len)return OP_EFAULT;
Chris@69 301 if(tag_len+value_len>(size_t)INT_MAX-2)return OP_EFAULT;
Chris@69 302 comment=(char *)_ogg_malloc(sizeof(*comment)*(tag_len+value_len+2));
Chris@69 303 if(OP_UNLIKELY(comment==NULL))return OP_EFAULT;
Chris@69 304 memcpy(comment,_tag,sizeof(*comment)*tag_len);
Chris@69 305 comment[tag_len]='=';
Chris@69 306 memcpy(comment+tag_len+1,_value,sizeof(*comment)*(value_len+1));
Chris@69 307 _tags->user_comments[ncomments]=comment;
Chris@69 308 _tags->comment_lengths[ncomments]=(int)(tag_len+value_len+1);
Chris@69 309 _tags->comments=ncomments+1;
Chris@69 310 return 0;
Chris@69 311 }
Chris@69 312
Chris@69 313 int opus_tags_add_comment(OpusTags *_tags,const char *_comment){
Chris@69 314 char *comment;
Chris@69 315 int comment_len;
Chris@69 316 int ncomments;
Chris@69 317 int ret;
Chris@69 318 ncomments=_tags->comments;
Chris@69 319 ret=op_tags_ensure_capacity(_tags,ncomments+1);
Chris@69 320 if(OP_UNLIKELY(ret<0))return ret;
Chris@69 321 comment_len=(int)strlen(_comment);
Chris@69 322 comment=op_strdup_with_len(_comment,comment_len);
Chris@69 323 if(OP_UNLIKELY(comment==NULL))return OP_EFAULT;
Chris@69 324 _tags->user_comments[ncomments]=comment;
Chris@69 325 _tags->comment_lengths[ncomments]=comment_len;
Chris@69 326 _tags->comments=ncomments+1;
Chris@69 327 return 0;
Chris@69 328 }
Chris@69 329
Chris@69 330 int opus_tags_set_binary_suffix(OpusTags *_tags,
Chris@69 331 const unsigned char *_data,int _len){
Chris@69 332 unsigned char *binary_suffix_data;
Chris@69 333 int ncomments;
Chris@69 334 int ret;
Chris@69 335 if(_len<0||_len>0&&(_data==NULL||!(_data[0]&1)))return OP_EINVAL;
Chris@69 336 ncomments=_tags->comments;
Chris@69 337 ret=op_tags_ensure_capacity(_tags,ncomments);
Chris@69 338 if(OP_UNLIKELY(ret<0))return ret;
Chris@69 339 binary_suffix_data=
Chris@69 340 (unsigned char *)_ogg_realloc(_tags->user_comments[ncomments],_len);
Chris@69 341 if(OP_UNLIKELY(binary_suffix_data==NULL))return OP_EFAULT;
Chris@69 342 memcpy(binary_suffix_data,_data,_len);
Chris@69 343 _tags->user_comments[ncomments]=(char *)binary_suffix_data;
Chris@69 344 _tags->comment_lengths[ncomments]=_len;
Chris@69 345 return 0;
Chris@69 346 }
Chris@69 347
Chris@69 348 int opus_tagcompare(const char *_tag_name,const char *_comment){
Chris@69 349 size_t tag_len;
Chris@69 350 tag_len=strlen(_tag_name);
Chris@69 351 if(OP_UNLIKELY(tag_len>(size_t)INT_MAX))return -1;
Chris@69 352 return opus_tagncompare(_tag_name,(int)tag_len,_comment);
Chris@69 353 }
Chris@69 354
Chris@69 355 int opus_tagncompare(const char *_tag_name,int _tag_len,const char *_comment){
Chris@69 356 int ret;
Chris@69 357 OP_ASSERT(_tag_len>=0);
Chris@69 358 ret=op_strncasecmp(_tag_name,_comment,_tag_len);
Chris@69 359 return ret?ret:'='-_comment[_tag_len];
Chris@69 360 }
Chris@69 361
Chris@69 362 const char *opus_tags_query(const OpusTags *_tags,const char *_tag,int _count){
Chris@69 363 char **user_comments;
Chris@69 364 size_t tag_len;
Chris@69 365 int found;
Chris@69 366 int ncomments;
Chris@69 367 int ci;
Chris@69 368 tag_len=strlen(_tag);
Chris@69 369 if(OP_UNLIKELY(tag_len>(size_t)INT_MAX))return NULL;
Chris@69 370 ncomments=_tags->comments;
Chris@69 371 user_comments=_tags->user_comments;
Chris@69 372 found=0;
Chris@69 373 for(ci=0;ci<ncomments;ci++){
Chris@69 374 if(!opus_tagncompare(_tag,(int)tag_len,user_comments[ci])){
Chris@69 375 /*We return a pointer to the data, not a copy.*/
Chris@69 376 if(_count==found++)return user_comments[ci]+tag_len+1;
Chris@69 377 }
Chris@69 378 }
Chris@69 379 /*Didn't find anything.*/
Chris@69 380 return NULL;
Chris@69 381 }
Chris@69 382
Chris@69 383 int opus_tags_query_count(const OpusTags *_tags,const char *_tag){
Chris@69 384 char **user_comments;
Chris@69 385 size_t tag_len;
Chris@69 386 int found;
Chris@69 387 int ncomments;
Chris@69 388 int ci;
Chris@69 389 tag_len=strlen(_tag);
Chris@69 390 if(OP_UNLIKELY(tag_len>(size_t)INT_MAX))return 0;
Chris@69 391 ncomments=_tags->comments;
Chris@69 392 user_comments=_tags->user_comments;
Chris@69 393 found=0;
Chris@69 394 for(ci=0;ci<ncomments;ci++){
Chris@69 395 if(!opus_tagncompare(_tag,(int)tag_len,user_comments[ci]))found++;
Chris@69 396 }
Chris@69 397 return found;
Chris@69 398 }
Chris@69 399
Chris@69 400 const unsigned char *opus_tags_get_binary_suffix(const OpusTags *_tags,
Chris@69 401 int *_len){
Chris@69 402 int ncomments;
Chris@69 403 int len;
Chris@69 404 ncomments=_tags->comments;
Chris@69 405 len=_tags->comment_lengths==NULL?0:_tags->comment_lengths[ncomments];
Chris@69 406 *_len=len;
Chris@69 407 OP_ASSERT(len==0||_tags->user_comments!=NULL);
Chris@69 408 return len>0?(const unsigned char *)_tags->user_comments[ncomments]:NULL;
Chris@69 409 }
Chris@69 410
Chris@69 411 static int opus_tags_get_gain(const OpusTags *_tags,int *_gain_q8,
Chris@69 412 const char *_tag_name,size_t _tag_len){
Chris@69 413 char **comments;
Chris@69 414 int ncomments;
Chris@69 415 int ci;
Chris@69 416 comments=_tags->user_comments;
Chris@69 417 ncomments=_tags->comments;
Chris@69 418 /*Look for the first valid tag with the name _tag_name and use that.*/
Chris@69 419 for(ci=0;ci<ncomments;ci++){
Chris@69 420 OP_ASSERT(_tag_len<=(size_t)INT_MAX);
Chris@69 421 if(opus_tagncompare(_tag_name,(int)_tag_len,comments[ci])==0){
Chris@69 422 char *p;
Chris@69 423 opus_int32 gain_q8;
Chris@69 424 int negative;
Chris@69 425 p=comments[ci]+_tag_len+1;
Chris@69 426 negative=0;
Chris@69 427 if(*p=='-'){
Chris@69 428 negative=-1;
Chris@69 429 p++;
Chris@69 430 }
Chris@69 431 else if(*p=='+')p++;
Chris@69 432 gain_q8=0;
Chris@69 433 while(*p>='0'&&*p<='9'){
Chris@69 434 gain_q8=10*gain_q8+*p-'0';
Chris@69 435 if(gain_q8>32767-negative)break;
Chris@69 436 p++;
Chris@69 437 }
Chris@69 438 /*This didn't look like a signed 16-bit decimal integer.
Chris@69 439 Not a valid gain tag.*/
Chris@69 440 if(*p!='\0')continue;
Chris@69 441 *_gain_q8=(int)(gain_q8+negative^negative);
Chris@69 442 return 0;
Chris@69 443 }
Chris@69 444 }
Chris@69 445 return OP_FALSE;
Chris@69 446 }
Chris@69 447
Chris@69 448 int opus_tags_get_album_gain(const OpusTags *_tags,int *_gain_q8){
Chris@69 449 return opus_tags_get_gain(_tags,_gain_q8,"R128_ALBUM_GAIN",15);
Chris@69 450 }
Chris@69 451
Chris@69 452 int opus_tags_get_track_gain(const OpusTags *_tags,int *_gain_q8){
Chris@69 453 return opus_tags_get_gain(_tags,_gain_q8,"R128_TRACK_GAIN",15);
Chris@69 454 }
Chris@69 455
Chris@69 456 static int op_is_jpeg(const unsigned char *_buf,size_t _buf_sz){
Chris@69 457 return _buf_sz>=11&&memcmp(_buf,"\xFF\xD8\xFF\xE0",4)==0
Chris@69 458 &&(_buf[4]<<8|_buf[5])>=16&&memcmp(_buf+6,"JFIF",5)==0;
Chris@69 459 }
Chris@69 460
Chris@69 461 /*Tries to extract the width, height, bits per pixel, and palette size of a
Chris@69 462 JPEG.
Chris@69 463 On failure, simply leaves its outputs unmodified.*/
Chris@69 464 static void op_extract_jpeg_params(const unsigned char *_buf,size_t _buf_sz,
Chris@69 465 opus_uint32 *_width,opus_uint32 *_height,
Chris@69 466 opus_uint32 *_depth,opus_uint32 *_colors,int *_has_palette){
Chris@69 467 if(op_is_jpeg(_buf,_buf_sz)){
Chris@69 468 size_t offs;
Chris@69 469 offs=2;
Chris@69 470 for(;;){
Chris@69 471 size_t segment_len;
Chris@69 472 int marker;
Chris@69 473 while(offs<_buf_sz&&_buf[offs]!=0xFF)offs++;
Chris@69 474 while(offs<_buf_sz&&_buf[offs]==0xFF)offs++;
Chris@69 475 marker=_buf[offs];
Chris@69 476 offs++;
Chris@69 477 /*If we hit EOI* (end of image), or another SOI* (start of image),
Chris@69 478 or SOS (start of scan), then stop now.*/
Chris@69 479 if(offs>=_buf_sz||(marker>=0xD8&&marker<=0xDA))break;
Chris@69 480 /*RST* (restart markers): skip (no segment length).*/
Chris@69 481 else if(marker>=0xD0&&marker<=0xD7)continue;
Chris@69 482 /*Read the length of the marker segment.*/
Chris@69 483 if(_buf_sz-offs<2)break;
Chris@69 484 segment_len=_buf[offs]<<8|_buf[offs+1];
Chris@69 485 if(segment_len<2||_buf_sz-offs<segment_len)break;
Chris@69 486 if(marker==0xC0||(marker>0xC0&&marker<0xD0&&(marker&3)!=0)){
Chris@69 487 /*Found a SOFn (start of frame) marker segment:*/
Chris@69 488 if(segment_len>=8){
Chris@69 489 *_height=_buf[offs+3]<<8|_buf[offs+4];
Chris@69 490 *_width=_buf[offs+5]<<8|_buf[offs+6];
Chris@69 491 *_depth=_buf[offs+2]*_buf[offs+7];
Chris@69 492 *_colors=0;
Chris@69 493 *_has_palette=0;
Chris@69 494 }
Chris@69 495 break;
Chris@69 496 }
Chris@69 497 /*Other markers: skip the whole marker segment.*/
Chris@69 498 offs+=segment_len;
Chris@69 499 }
Chris@69 500 }
Chris@69 501 }
Chris@69 502
Chris@69 503 static int op_is_png(const unsigned char *_buf,size_t _buf_sz){
Chris@69 504 return _buf_sz>=8&&memcmp(_buf,"\x89PNG\x0D\x0A\x1A\x0A",8)==0;
Chris@69 505 }
Chris@69 506
Chris@69 507 /*Tries to extract the width, height, bits per pixel, and palette size of a
Chris@69 508 PNG.
Chris@69 509 On failure, simply leaves its outputs unmodified.*/
Chris@69 510 static void op_extract_png_params(const unsigned char *_buf,size_t _buf_sz,
Chris@69 511 opus_uint32 *_width,opus_uint32 *_height,
Chris@69 512 opus_uint32 *_depth,opus_uint32 *_colors,int *_has_palette){
Chris@69 513 if(op_is_png(_buf,_buf_sz)){
Chris@69 514 size_t offs;
Chris@69 515 offs=8;
Chris@69 516 while(_buf_sz-offs>=12){
Chris@69 517 ogg_uint32_t chunk_len;
Chris@69 518 chunk_len=op_parse_uint32be(_buf+offs);
Chris@69 519 if(chunk_len>_buf_sz-(offs+12))break;
Chris@69 520 else if(chunk_len==13&&memcmp(_buf+offs+4,"IHDR",4)==0){
Chris@69 521 int color_type;
Chris@69 522 *_width=op_parse_uint32be(_buf+offs+8);
Chris@69 523 *_height=op_parse_uint32be(_buf+offs+12);
Chris@69 524 color_type=_buf[offs+17];
Chris@69 525 if(color_type==3){
Chris@69 526 *_depth=24;
Chris@69 527 *_has_palette=1;
Chris@69 528 }
Chris@69 529 else{
Chris@69 530 int sample_depth;
Chris@69 531 sample_depth=_buf[offs+16];
Chris@69 532 if(color_type==0)*_depth=sample_depth;
Chris@69 533 else if(color_type==2)*_depth=sample_depth*3;
Chris@69 534 else if(color_type==4)*_depth=sample_depth*2;
Chris@69 535 else if(color_type==6)*_depth=sample_depth*4;
Chris@69 536 *_colors=0;
Chris@69 537 *_has_palette=0;
Chris@69 538 break;
Chris@69 539 }
Chris@69 540 }
Chris@69 541 else if(*_has_palette>0&&memcmp(_buf+offs+4,"PLTE",4)==0){
Chris@69 542 *_colors=chunk_len/3;
Chris@69 543 break;
Chris@69 544 }
Chris@69 545 offs+=12+chunk_len;
Chris@69 546 }
Chris@69 547 }
Chris@69 548 }
Chris@69 549
Chris@69 550 static int op_is_gif(const unsigned char *_buf,size_t _buf_sz){
Chris@69 551 return _buf_sz>=6&&(memcmp(_buf,"GIF87a",6)==0||memcmp(_buf,"GIF89a",6)==0);
Chris@69 552 }
Chris@69 553
Chris@69 554 /*Tries to extract the width, height, bits per pixel, and palette size of a
Chris@69 555 GIF.
Chris@69 556 On failure, simply leaves its outputs unmodified.*/
Chris@69 557 static void op_extract_gif_params(const unsigned char *_buf,size_t _buf_sz,
Chris@69 558 opus_uint32 *_width,opus_uint32 *_height,
Chris@69 559 opus_uint32 *_depth,opus_uint32 *_colors,int *_has_palette){
Chris@69 560 if(op_is_gif(_buf,_buf_sz)&&_buf_sz>=14){
Chris@69 561 *_width=_buf[6]|_buf[7]<<8;
Chris@69 562 *_height=_buf[8]|_buf[9]<<8;
Chris@69 563 /*libFLAC hard-codes the depth to 24.*/
Chris@69 564 *_depth=24;
Chris@69 565 *_colors=1<<((_buf[10]&7)+1);
Chris@69 566 *_has_palette=1;
Chris@69 567 }
Chris@69 568 }
Chris@69 569
Chris@69 570 /*The actual implementation of opus_picture_tag_parse().
Chris@69 571 Unlike the public API, this function requires _pic to already be
Chris@69 572 initialized, modifies its contents before success is guaranteed, and assumes
Chris@69 573 the caller will clear it on error.*/
Chris@69 574 static int opus_picture_tag_parse_impl(OpusPictureTag *_pic,const char *_tag,
Chris@69 575 unsigned char *_buf,size_t _buf_sz,size_t _base64_sz){
Chris@69 576 opus_int32 picture_type;
Chris@69 577 opus_uint32 mime_type_length;
Chris@69 578 char *mime_type;
Chris@69 579 opus_uint32 description_length;
Chris@69 580 char *description;
Chris@69 581 opus_uint32 width;
Chris@69 582 opus_uint32 height;
Chris@69 583 opus_uint32 depth;
Chris@69 584 opus_uint32 colors;
Chris@69 585 opus_uint32 data_length;
Chris@69 586 opus_uint32 file_width;
Chris@69 587 opus_uint32 file_height;
Chris@69 588 opus_uint32 file_depth;
Chris@69 589 opus_uint32 file_colors;
Chris@69 590 int format;
Chris@69 591 int has_palette;
Chris@69 592 int colors_set;
Chris@69 593 size_t i;
Chris@69 594 /*Decode the BASE64 data.*/
Chris@69 595 for(i=0;i<_base64_sz;i++){
Chris@69 596 opus_uint32 value;
Chris@69 597 int j;
Chris@69 598 value=0;
Chris@69 599 for(j=0;j<4;j++){
Chris@69 600 unsigned c;
Chris@69 601 unsigned d;
Chris@69 602 c=(unsigned char)_tag[4*i+j];
Chris@69 603 if(c=='+')d=62;
Chris@69 604 else if(c=='/')d=63;
Chris@69 605 else if(c>='0'&&c<='9')d=52+c-'0';
Chris@69 606 else if(c>='a'&&c<='z')d=26+c-'a';
Chris@69 607 else if(c>='A'&&c<='Z')d=c-'A';
Chris@69 608 else if(c=='='&&3*i+j>_buf_sz)d=0;
Chris@69 609 else return OP_ENOTFORMAT;
Chris@69 610 value=value<<6|d;
Chris@69 611 }
Chris@69 612 _buf[3*i]=(unsigned char)(value>>16);
Chris@69 613 if(3*i+1<_buf_sz){
Chris@69 614 _buf[3*i+1]=(unsigned char)(value>>8);
Chris@69 615 if(3*i+2<_buf_sz)_buf[3*i+2]=(unsigned char)value;
Chris@69 616 }
Chris@69 617 }
Chris@69 618 i=0;
Chris@69 619 picture_type=op_parse_uint32be(_buf+i);
Chris@69 620 i+=4;
Chris@69 621 /*Extract the MIME type.*/
Chris@69 622 mime_type_length=op_parse_uint32be(_buf+i);
Chris@69 623 i+=4;
Chris@69 624 if(mime_type_length>_buf_sz-32)return OP_ENOTFORMAT;
Chris@69 625 mime_type=(char *)_ogg_malloc(sizeof(*_pic->mime_type)*(mime_type_length+1));
Chris@69 626 if(mime_type==NULL)return OP_EFAULT;
Chris@69 627 memcpy(mime_type,_buf+i,sizeof(*mime_type)*mime_type_length);
Chris@69 628 mime_type[mime_type_length]='\0';
Chris@69 629 _pic->mime_type=mime_type;
Chris@69 630 i+=mime_type_length;
Chris@69 631 /*Extract the description string.*/
Chris@69 632 description_length=op_parse_uint32be(_buf+i);
Chris@69 633 i+=4;
Chris@69 634 if(description_length>_buf_sz-mime_type_length-32)return OP_ENOTFORMAT;
Chris@69 635 description=
Chris@69 636 (char *)_ogg_malloc(sizeof(*_pic->mime_type)*(description_length+1));
Chris@69 637 if(description==NULL)return OP_EFAULT;
Chris@69 638 memcpy(description,_buf+i,sizeof(*description)*description_length);
Chris@69 639 description[description_length]='\0';
Chris@69 640 _pic->description=description;
Chris@69 641 i+=description_length;
Chris@69 642 /*Extract the remaining fields.*/
Chris@69 643 width=op_parse_uint32be(_buf+i);
Chris@69 644 i+=4;
Chris@69 645 height=op_parse_uint32be(_buf+i);
Chris@69 646 i+=4;
Chris@69 647 depth=op_parse_uint32be(_buf+i);
Chris@69 648 i+=4;
Chris@69 649 colors=op_parse_uint32be(_buf+i);
Chris@69 650 i+=4;
Chris@69 651 /*If one of these is set, they all must be, but colors==0 is a valid value.*/
Chris@69 652 colors_set=width!=0||height!=0||depth!=0||colors!=0;
Chris@69 653 if((width==0||height==0||depth==0)&&colors_set)return OP_ENOTFORMAT;
Chris@69 654 data_length=op_parse_uint32be(_buf+i);
Chris@69 655 i+=4;
Chris@69 656 if(data_length>_buf_sz-i)return OP_ENOTFORMAT;
Chris@69 657 /*Trim extraneous data so we don't copy it below.*/
Chris@69 658 _buf_sz=i+data_length;
Chris@69 659 /*Attempt to determine the image format.*/
Chris@69 660 format=OP_PIC_FORMAT_UNKNOWN;
Chris@69 661 if(mime_type_length==3&&strcmp(mime_type,"-->")==0){
Chris@69 662 format=OP_PIC_FORMAT_URL;
Chris@69 663 /*Picture type 1 must be a 32x32 PNG.*/
Chris@69 664 if(picture_type==1&&(width!=0||height!=0)&&(width!=32||height!=32)){
Chris@69 665 return OP_ENOTFORMAT;
Chris@69 666 }
Chris@69 667 /*Append a terminating NUL for the convenience of our callers.*/
Chris@69 668 _buf[_buf_sz++]='\0';
Chris@69 669 }
Chris@69 670 else{
Chris@69 671 if(mime_type_length==10
Chris@69 672 &&op_strncasecmp(mime_type,"image/jpeg",mime_type_length)==0){
Chris@69 673 if(op_is_jpeg(_buf+i,data_length))format=OP_PIC_FORMAT_JPEG;
Chris@69 674 }
Chris@69 675 else if(mime_type_length==9
Chris@69 676 &&op_strncasecmp(mime_type,"image/png",mime_type_length)==0){
Chris@69 677 if(op_is_png(_buf+i,data_length))format=OP_PIC_FORMAT_PNG;
Chris@69 678 }
Chris@69 679 else if(mime_type_length==9
Chris@69 680 &&op_strncasecmp(mime_type,"image/gif",mime_type_length)==0){
Chris@69 681 if(op_is_gif(_buf+i,data_length))format=OP_PIC_FORMAT_GIF;
Chris@69 682 }
Chris@69 683 else if(mime_type_length==0||(mime_type_length==6
Chris@69 684 &&op_strncasecmp(mime_type,"image/",mime_type_length)==0)){
Chris@69 685 if(op_is_jpeg(_buf+i,data_length))format=OP_PIC_FORMAT_JPEG;
Chris@69 686 else if(op_is_png(_buf+i,data_length))format=OP_PIC_FORMAT_PNG;
Chris@69 687 else if(op_is_gif(_buf+i,data_length))format=OP_PIC_FORMAT_GIF;
Chris@69 688 }
Chris@69 689 file_width=file_height=file_depth=file_colors=0;
Chris@69 690 has_palette=-1;
Chris@69 691 switch(format){
Chris@69 692 case OP_PIC_FORMAT_JPEG:{
Chris@69 693 op_extract_jpeg_params(_buf+i,data_length,
Chris@69 694 &file_width,&file_height,&file_depth,&file_colors,&has_palette);
Chris@69 695 }break;
Chris@69 696 case OP_PIC_FORMAT_PNG:{
Chris@69 697 op_extract_png_params(_buf+i,data_length,
Chris@69 698 &file_width,&file_height,&file_depth,&file_colors,&has_palette);
Chris@69 699 }break;
Chris@69 700 case OP_PIC_FORMAT_GIF:{
Chris@69 701 op_extract_gif_params(_buf+i,data_length,
Chris@69 702 &file_width,&file_height,&file_depth,&file_colors,&has_palette);
Chris@69 703 }break;
Chris@69 704 }
Chris@69 705 if(has_palette>=0){
Chris@69 706 /*If we successfully extracted these parameters from the image, override
Chris@69 707 any declared values.*/
Chris@69 708 width=file_width;
Chris@69 709 height=file_height;
Chris@69 710 depth=file_depth;
Chris@69 711 colors=file_colors;
Chris@69 712 }
Chris@69 713 /*Picture type 1 must be a 32x32 PNG.*/
Chris@69 714 if(picture_type==1&&(format!=OP_PIC_FORMAT_PNG||width!=32||height!=32)){
Chris@69 715 return OP_ENOTFORMAT;
Chris@69 716 }
Chris@69 717 }
Chris@69 718 /*Adjust _buf_sz instead of using data_length to capture the terminating NUL
Chris@69 719 for URLs.*/
Chris@69 720 _buf_sz-=i;
Chris@69 721 memmove(_buf,_buf+i,sizeof(*_buf)*_buf_sz);
Chris@69 722 _buf=(unsigned char *)_ogg_realloc(_buf,_buf_sz);
Chris@69 723 if(_buf_sz>0&&_buf==NULL)return OP_EFAULT;
Chris@69 724 _pic->type=picture_type;
Chris@69 725 _pic->width=width;
Chris@69 726 _pic->height=height;
Chris@69 727 _pic->depth=depth;
Chris@69 728 _pic->colors=colors;
Chris@69 729 _pic->data_length=data_length;
Chris@69 730 _pic->data=_buf;
Chris@69 731 _pic->format=format;
Chris@69 732 return 0;
Chris@69 733 }
Chris@69 734
Chris@69 735 int opus_picture_tag_parse(OpusPictureTag *_pic,const char *_tag){
Chris@69 736 OpusPictureTag pic;
Chris@69 737 unsigned char *buf;
Chris@69 738 size_t base64_sz;
Chris@69 739 size_t buf_sz;
Chris@69 740 size_t tag_length;
Chris@69 741 int ret;
Chris@69 742 if(opus_tagncompare("METADATA_BLOCK_PICTURE",22,_tag)==0)_tag+=23;
Chris@69 743 /*Figure out how much BASE64-encoded data we have.*/
Chris@69 744 tag_length=strlen(_tag);
Chris@69 745 if(tag_length&3)return OP_ENOTFORMAT;
Chris@69 746 base64_sz=tag_length>>2;
Chris@69 747 buf_sz=3*base64_sz;
Chris@69 748 if(buf_sz<32)return OP_ENOTFORMAT;
Chris@69 749 if(_tag[tag_length-1]=='=')buf_sz--;
Chris@69 750 if(_tag[tag_length-2]=='=')buf_sz--;
Chris@69 751 if(buf_sz<32)return OP_ENOTFORMAT;
Chris@69 752 /*Allocate an extra byte to allow appending a terminating NUL to URL data.*/
Chris@69 753 buf=(unsigned char *)_ogg_malloc(sizeof(*buf)*(buf_sz+1));
Chris@69 754 if(buf==NULL)return OP_EFAULT;
Chris@69 755 opus_picture_tag_init(&pic);
Chris@69 756 ret=opus_picture_tag_parse_impl(&pic,_tag,buf,buf_sz,base64_sz);
Chris@69 757 if(ret<0){
Chris@69 758 opus_picture_tag_clear(&pic);
Chris@69 759 _ogg_free(buf);
Chris@69 760 }
Chris@69 761 else *_pic=*&pic;
Chris@69 762 return ret;
Chris@69 763 }
Chris@69 764
Chris@69 765 void opus_picture_tag_init(OpusPictureTag *_pic){
Chris@69 766 memset(_pic,0,sizeof(*_pic));
Chris@69 767 }
Chris@69 768
Chris@69 769 void opus_picture_tag_clear(OpusPictureTag *_pic){
Chris@69 770 _ogg_free(_pic->description);
Chris@69 771 _ogg_free(_pic->mime_type);
Chris@69 772 _ogg_free(_pic->data);
Chris@69 773 }