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 }
|