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