Line | Count | Source |
1 | | /******************************************************************** |
2 | | * * |
3 | | * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * |
4 | | * * |
5 | | * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * |
6 | | * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * |
7 | | * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * |
8 | | * * |
9 | | * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * |
10 | | * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * |
11 | | * * |
12 | | ******************************************************************** |
13 | | |
14 | | function: PCM data vector blocking, windowing and dis/reassembly |
15 | | |
16 | | ********************************************************************/ |
17 | | |
18 | | #include <stdio.h> |
19 | | #include <stdlib.h> |
20 | | #include <string.h> |
21 | | #include <ogg/ogg.h> |
22 | | #include "ivorbiscodec.h" |
23 | | #include "codec_internal.h" |
24 | | |
25 | | #include "window.h" |
26 | | #include "registry.h" |
27 | | #include "misc.h" |
28 | | |
29 | 9.65k | static int ilog(unsigned int v){ |
30 | 9.65k | int ret=0; |
31 | 9.65k | if(v)--v; |
32 | 10.5k | while(v){ |
33 | 848 | ret++; |
34 | 848 | v>>=1; |
35 | 848 | } |
36 | 9.65k | return(ret); |
37 | 9.65k | } |
38 | | |
39 | | /* pcm accumulator examples (not exhaustive): |
40 | | |
41 | | <-------------- lW ----------------> |
42 | | <--------------- W ----------------> |
43 | | : .....|..... _______________ | |
44 | | : .''' | '''_--- | |\ | |
45 | | :.....''' |_____--- '''......| | \_______| |
46 | | :.................|__________________|_______|__|______| |
47 | | |<------ Sl ------>| > Sr < |endW |
48 | | |beginSl |endSl | |endSr |
49 | | |beginW |endlW |beginSr |
50 | | |
51 | | |
52 | | |< lW >| |
53 | | <--------------- W ----------------> |
54 | | | | .. ______________ | |
55 | | | | ' `/ | ---_ | |
56 | | |___.'___/`. | ---_____| |
57 | | |_______|__|_______|_________________| |
58 | | | >|Sl|< |<------ Sr ----->|endW |
59 | | | | |endSl |beginSr |endSr |
60 | | |beginW | |endlW |
61 | | mult[0] |beginSl mult[n] |
62 | | |
63 | | <-------------- lW -----------------> |
64 | | |<--W-->| |
65 | | : .............. ___ | | |
66 | | : .''' |`/ \ | | |
67 | | :.....''' |/`....\|...| |
68 | | :.........................|___|___|___| |
69 | | |Sl |Sr |endW |
70 | | | | |endSr |
71 | | | |beginSr |
72 | | | |endSl |
73 | | |beginSl |
74 | | |beginW |
75 | | */ |
76 | | |
77 | | /* block abstraction setup *********************************************/ |
78 | | |
79 | | #ifndef WORD_ALIGN |
80 | 10.4M | #define WORD_ALIGN 8 |
81 | | #endif |
82 | | |
83 | 9.51k | int vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb){ |
84 | 9.51k | memset(vb,0,sizeof(*vb)); |
85 | 9.51k | vb->vd=v; |
86 | 9.51k | vb->localalloc=0; |
87 | 9.51k | vb->localstore=NULL; |
88 | | |
89 | 9.51k | return(0); |
90 | 9.51k | } |
91 | | |
92 | 5.20M | void *_vorbis_block_alloc(vorbis_block *vb,long bytes){ |
93 | 5.20M | bytes=(bytes+(WORD_ALIGN-1)) & ~(WORD_ALIGN-1); |
94 | 5.20M | if(bytes+vb->localtop>vb->localalloc){ |
95 | | /* can't just _ogg_realloc... there are outstanding pointers */ |
96 | 468k | if(vb->localstore){ |
97 | 462k | struct alloc_chain *link=(struct alloc_chain *)_ogg_malloc(sizeof(*link)); |
98 | 462k | vb->totaluse+=vb->localtop; |
99 | 462k | link->next=vb->reap; |
100 | 462k | link->ptr=vb->localstore; |
101 | 462k | vb->reap=link; |
102 | 462k | } |
103 | | /* highly conservative */ |
104 | 468k | vb->localalloc=bytes; |
105 | 468k | vb->localstore=_ogg_malloc(vb->localalloc); |
106 | 468k | vb->localtop=0; |
107 | 468k | } |
108 | 5.20M | { |
109 | 5.20M | void *ret=(void *)(((char *)vb->localstore)+vb->localtop); |
110 | 5.20M | vb->localtop+=bytes; |
111 | 5.20M | return ret; |
112 | 5.20M | } |
113 | 5.20M | } |
114 | | |
115 | | /* reap the chain, pull the ripcord */ |
116 | 128k | void _vorbis_block_ripcord(vorbis_block *vb){ |
117 | | /* reap the chain */ |
118 | 128k | struct alloc_chain *reap=vb->reap; |
119 | 590k | while(reap){ |
120 | 462k | struct alloc_chain *next=reap->next; |
121 | 462k | _ogg_free(reap->ptr); |
122 | 462k | memset(reap,0,sizeof(*reap)); |
123 | 462k | _ogg_free(reap); |
124 | 462k | reap=next; |
125 | 462k | } |
126 | | /* consolidate storage */ |
127 | 128k | if(vb->totaluse){ |
128 | 7.78k | vb->localstore=_ogg_realloc(vb->localstore,vb->totaluse+vb->localalloc); |
129 | 7.78k | vb->localalloc+=vb->totaluse; |
130 | 7.78k | vb->totaluse=0; |
131 | 7.78k | } |
132 | | |
133 | | /* pull the ripcord */ |
134 | 128k | vb->localtop=0; |
135 | 128k | vb->reap=NULL; |
136 | 128k | } |
137 | | |
138 | 11.8k | int vorbis_block_clear(vorbis_block *vb){ |
139 | 11.8k | _vorbis_block_ripcord(vb); |
140 | 11.8k | if(vb->localstore)_ogg_free(vb->localstore); |
141 | | |
142 | 11.8k | memset(vb,0,sizeof(*vb)); |
143 | 11.8k | return(0); |
144 | 11.8k | } |
145 | | |
146 | 10.8k | static int _vds_init(vorbis_dsp_state *v,vorbis_info *vi){ |
147 | 10.8k | int i; |
148 | 10.8k | codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; |
149 | 10.8k | private_state *b=NULL; |
150 | | |
151 | 10.8k | if(ci==NULL) return 1; |
152 | | |
153 | 9.65k | memset(v,0,sizeof(*v)); |
154 | 9.65k | b=(private_state *)(v->backend_state=_ogg_calloc(1,sizeof(*b))); |
155 | | |
156 | 9.65k | v->vi=vi; |
157 | 9.65k | b->modebits=ilog(ci->modes); |
158 | | |
159 | | /* Vorbis I uses only window type 0 */ |
160 | 9.65k | b->window[0]=_vorbis_window(0,ci->blocksizes[0]/2); |
161 | 9.65k | b->window[1]=_vorbis_window(0,ci->blocksizes[1]/2); |
162 | | |
163 | | /* finish the codebooks */ |
164 | 9.65k | if(!ci->fullbooks){ |
165 | 9.65k | ci->fullbooks=(codebook *)_ogg_calloc(ci->books,sizeof(*ci->fullbooks)); |
166 | 22.7k | for(i=0;i<ci->books;i++){ |
167 | 13.2k | if(ci->book_param[i]==NULL) |
168 | 0 | goto abort_books; |
169 | 13.2k | if(vorbis_book_init_decode(ci->fullbooks+i,ci->book_param[i])) |
170 | 141 | goto abort_books; |
171 | | /* decode codebooks are now standalone after init */ |
172 | 13.0k | vorbis_staticbook_destroy(ci->book_param[i]); |
173 | 13.0k | ci->book_param[i]=NULL; |
174 | 13.0k | } |
175 | 9.65k | } |
176 | | |
177 | 9.51k | v->pcm_storage=ci->blocksizes[1]; |
178 | 9.51k | v->pcm=(ogg_int32_t **)_ogg_malloc(vi->channels*sizeof(*v->pcm)); |
179 | 9.51k | v->pcmret=(ogg_int32_t **)_ogg_malloc(vi->channels*sizeof(*v->pcmret)); |
180 | 702k | for(i=0;i<vi->channels;i++) |
181 | 692k | v->pcm[i]=(ogg_int32_t *)_ogg_calloc(v->pcm_storage,sizeof(*v->pcm[i])); |
182 | | |
183 | | /* all 1 (large block) or 0 (small block) */ |
184 | | /* explicitly set for the sake of clarity */ |
185 | 9.51k | v->lW=0; /* previous window size */ |
186 | 9.51k | v->W=0; /* current window size */ |
187 | | |
188 | | /* initialize all the mapping/backend lookups */ |
189 | 9.51k | b->mode=(vorbis_look_mapping **)_ogg_calloc(ci->modes,sizeof(*b->mode)); |
190 | 19.9k | for(i=0;i<ci->modes;i++){ |
191 | 10.4k | int mapnum=ci->mode_param[i]->mapping; |
192 | 10.4k | int maptype=ci->map_type[mapnum]; |
193 | 10.4k | b->mode[i]=_mapping_P[maptype]->look(v,ci->mode_param[i], |
194 | 10.4k | ci->map_param[mapnum]); |
195 | 10.4k | } |
196 | 9.51k | return 0; |
197 | 141 | abort_books: |
198 | 2.01k | for(i=0;i<ci->books;i++){ |
199 | 1.87k | if(ci->book_param[i]!=NULL){ |
200 | 805 | vorbis_staticbook_destroy(ci->book_param[i]); |
201 | 805 | ci->book_param[i]=NULL; |
202 | 805 | } |
203 | 1.87k | } |
204 | 141 | vorbis_dsp_clear(v); |
205 | 141 | return -1; |
206 | 9.65k | } |
207 | | |
208 | 9.51k | int vorbis_synthesis_restart(vorbis_dsp_state *v){ |
209 | 9.51k | vorbis_info *vi=v->vi; |
210 | 9.51k | codec_setup_info *ci; |
211 | | |
212 | 9.51k | if(!v->backend_state)return -1; |
213 | 9.51k | if(!vi)return -1; |
214 | 9.51k | ci=vi->codec_setup; |
215 | 9.51k | if(!ci)return -1; |
216 | | |
217 | 9.51k | v->centerW=ci->blocksizes[1]/2; |
218 | 9.51k | v->pcm_current=v->centerW; |
219 | | |
220 | 9.51k | v->pcm_returned=-1; |
221 | 9.51k | v->granulepos=-1; |
222 | 9.51k | v->sequence=-1; |
223 | 9.51k | ((private_state *)(v->backend_state))->sample_count=-1; |
224 | | |
225 | 9.51k | return(0); |
226 | 9.51k | } |
227 | | |
228 | 10.8k | int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi){ |
229 | 10.8k | if(_vds_init(v,vi))return 1; |
230 | 9.51k | vorbis_synthesis_restart(v); |
231 | | |
232 | 9.51k | return 0; |
233 | 10.8k | } |
234 | | |
235 | 11.9k | void vorbis_dsp_clear(vorbis_dsp_state *v){ |
236 | 11.9k | int i; |
237 | 11.9k | if(v){ |
238 | 11.9k | vorbis_info *vi=v->vi; |
239 | 11.9k | codec_setup_info *ci=(codec_setup_info *)(vi?vi->codec_setup:NULL); |
240 | 11.9k | private_state *b=(private_state *)v->backend_state; |
241 | | |
242 | 11.9k | if(v->pcm){ |
243 | 702k | for(i=0;i<vi->channels;i++) |
244 | 692k | if(v->pcm[i])_ogg_free(v->pcm[i]); |
245 | 9.51k | _ogg_free(v->pcm); |
246 | 9.51k | if(v->pcmret)_ogg_free(v->pcmret); |
247 | 9.51k | } |
248 | | |
249 | | /* free mode lookups; these are actually vorbis_look_mapping structs */ |
250 | 11.9k | if(ci){ |
251 | 20.4k | for(i=0;i<ci->modes;i++){ |
252 | 10.8k | int mapnum=ci->mode_param[i]->mapping; |
253 | 10.8k | int maptype=ci->map_type[mapnum]; |
254 | 10.8k | if(b && b->mode)_mapping_P[maptype]->free_look(b->mode[i]); |
255 | 10.8k | } |
256 | 9.65k | } |
257 | | |
258 | 11.9k | if(b){ |
259 | 9.65k | if(b->mode)_ogg_free(b->mode); |
260 | 9.65k | _ogg_free(b); |
261 | 9.65k | } |
262 | | |
263 | 11.9k | memset(v,0,sizeof(*v)); |
264 | 11.9k | } |
265 | 11.9k | } |
266 | | |
267 | | /* Unlike in analysis, the window is only partially applied for each |
268 | | block. The time domain envelope is not yet handled at the point of |
269 | | calling (as it relies on the previous block). */ |
270 | | |
271 | 53.2k | int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ |
272 | 53.2k | vorbis_info *vi=v->vi; |
273 | 53.2k | codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; |
274 | 53.2k | private_state *b=v->backend_state; |
275 | 53.2k | int i,j; |
276 | | |
277 | 53.2k | if(v->pcm_current>v->pcm_returned && v->pcm_returned!=-1)return(OV_EINVAL); |
278 | | |
279 | 51.2k | v->lW=v->W; |
280 | 51.2k | v->W=vb->W; |
281 | 51.2k | v->nW=-1; |
282 | | |
283 | 51.2k | if((v->sequence==-1)|| |
284 | 44.8k | (v->sequence+1 != vb->sequence)){ |
285 | 26.1k | v->granulepos=-1; /* out of sequence; lose count */ |
286 | 26.1k | b->sample_count=-1; |
287 | 26.1k | } |
288 | | |
289 | 51.2k | v->sequence=vb->sequence; |
290 | | |
291 | 51.2k | if(vb->pcm){ /* no pcm to process if vorbis_synthesis_trackonly |
292 | | was called on block */ |
293 | 51.2k | int n=ci->blocksizes[v->W]/2; |
294 | 51.2k | int n0=ci->blocksizes[0]/2; |
295 | 51.2k | int n1=ci->blocksizes[1]/2; |
296 | | |
297 | 51.2k | int thisCenter; |
298 | 51.2k | int prevCenter; |
299 | | |
300 | 51.2k | if(v->centerW){ |
301 | 27.5k | thisCenter=n1; |
302 | 27.5k | prevCenter=0; |
303 | 27.5k | }else{ |
304 | 23.6k | thisCenter=0; |
305 | 23.6k | prevCenter=n1; |
306 | 23.6k | } |
307 | | |
308 | | /* v->pcm is now used like a two-stage double buffer. We don't want |
309 | | to have to constantly shift *or* adjust memory usage. Don't |
310 | | accept a new block until the old is shifted out */ |
311 | | |
312 | | /* overlap/add PCM */ |
313 | | |
314 | 4.89M | for(j=0;j<vi->channels;j++){ |
315 | | /* the overlap/add section */ |
316 | 4.84M | if(v->lW){ |
317 | 949k | if(v->W){ |
318 | | /* large/large */ |
319 | 937k | ogg_int32_t *pcm=v->pcm[j]+prevCenter; |
320 | 937k | ogg_int32_t *p=vb->pcm[j]; |
321 | 1.57G | for(i=0;i<n1;i++) |
322 | 1.57G | pcm[i]+=p[i]; |
323 | 937k | }else{ |
324 | | /* large/small */ |
325 | 11.9k | ogg_int32_t *pcm=v->pcm[j]+prevCenter+n1/2-n0/2; |
326 | 11.9k | ogg_int32_t *p=vb->pcm[j]; |
327 | 8.50M | for(i=0;i<n0;i++) |
328 | 8.48M | pcm[i]+=p[i]; |
329 | 11.9k | } |
330 | 3.89M | }else{ |
331 | 3.89M | if(v->W){ |
332 | | /* small/large */ |
333 | 177k | ogg_int32_t *pcm=v->pcm[j]+prevCenter; |
334 | 177k | ogg_int32_t *p=vb->pcm[j]+n1/2-n0/2; |
335 | 50.5M | for(i=0;i<n0;i++) |
336 | 50.3M | pcm[i]+=p[i]; |
337 | 62.6M | for(;i<n1/2+n0/2;i++) |
338 | 62.4M | pcm[i]=p[i]; |
339 | 3.71M | }else{ |
340 | | /* small/small */ |
341 | 3.71M | ogg_int32_t *pcm=v->pcm[j]+prevCenter; |
342 | 3.71M | ogg_int32_t *p=vb->pcm[j]; |
343 | 1.62G | for(i=0;i<n0;i++) |
344 | 1.61G | pcm[i]+=p[i]; |
345 | 3.71M | } |
346 | 3.89M | } |
347 | | |
348 | | /* the copy section */ |
349 | 4.84M | { |
350 | 4.84M | ogg_int32_t *pcm=v->pcm[j]+thisCenter; |
351 | 4.84M | ogg_int32_t *p=vb->pcm[j]+n; |
352 | 3.37G | for(i=0;i<n;i++) |
353 | 3.37G | pcm[i]=p[i]; |
354 | 4.84M | } |
355 | 4.84M | } |
356 | | |
357 | 51.2k | if(v->centerW) |
358 | 27.5k | v->centerW=0; |
359 | 23.6k | else |
360 | 23.6k | v->centerW=n1; |
361 | | |
362 | | /* deal with initial packet state; we do this using the explicit |
363 | | pcm_returned==-1 flag otherwise we're sensitive to first block |
364 | | being short or long */ |
365 | | |
366 | 51.2k | if(v->pcm_returned==-1){ |
367 | 6.74k | v->pcm_returned=thisCenter; |
368 | 6.74k | v->pcm_current=thisCenter; |
369 | 44.4k | }else{ |
370 | 44.4k | v->pcm_returned=prevCenter; |
371 | 44.4k | v->pcm_current=prevCenter+ |
372 | 44.4k | ci->blocksizes[v->lW]/4+ |
373 | 44.4k | ci->blocksizes[v->W]/4; |
374 | 44.4k | } |
375 | | |
376 | 51.2k | } |
377 | | |
378 | | /* track the frame number... This is for convenience, but also |
379 | | making sure our last packet doesn't end with added padding. If |
380 | | the last packet is partial, the number of samples we'll have to |
381 | | return will be past the vb->granulepos. |
382 | | |
383 | | This is not foolproof! It will be confused if we begin |
384 | | decoding at the last page after a seek or hole. In that case, |
385 | | we don't have a starting point to judge where the last frame |
386 | | is. For this reason, vorbisfile will always try to make sure |
387 | | it reads the last two marked pages in proper sequence */ |
388 | | |
389 | 51.2k | if(b->sample_count==-1){ |
390 | 26.1k | b->sample_count=0; |
391 | 26.1k | }else{ |
392 | 25.0k | b->sample_count+=ci->blocksizes[v->lW]/4+ci->blocksizes[v->W]/4; |
393 | 25.0k | } |
394 | | |
395 | 51.2k | if(v->granulepos==-1){ |
396 | 48.4k | if(vb->granulepos!=-1){ /* only set if we have a position to set to */ |
397 | | |
398 | 9.62k | v->granulepos=vb->granulepos; |
399 | | |
400 | | /* is this a short page? */ |
401 | 9.62k | if(b->sample_count>v->granulepos){ |
402 | | /* corner case; if this is both the first and last audio page, |
403 | | then spec says the end is cut, not beginning */ |
404 | 6.09k | long extra=b->sample_count-vb->granulepos; |
405 | | |
406 | | /* we use ogg_int64_t for granule positions because a |
407 | | uint64 isn't universally available. Unfortunately, |
408 | | that means granposes can be 'negative' and result in |
409 | | extra being negative */ |
410 | 6.09k | if(extra<0) |
411 | 259 | extra=0; |
412 | | |
413 | 6.09k | if(vb->eofflag){ |
414 | | /* trim the end */ |
415 | | /* no preceeding granulepos; assume we started at zero (we'd |
416 | | have to in a short single-page stream) */ |
417 | | /* granulepos could be -1 due to a seek, but that would result |
418 | | in a long coun`t, not short count */ |
419 | | |
420 | | /* Guard against corrupt/malicious frames that set EOP and |
421 | | a backdated granpos; don't rewind more samples than we |
422 | | actually have */ |
423 | 2.76k | if(extra > v->pcm_current - v->pcm_returned) |
424 | 2.48k | extra = v->pcm_current - v->pcm_returned; |
425 | | |
426 | 2.76k | v->pcm_current-=extra; |
427 | 3.32k | }else{ |
428 | | /* trim the beginning */ |
429 | 3.32k | v->pcm_returned+=extra; |
430 | 3.32k | if(v->pcm_returned>v->pcm_current) |
431 | 1.51k | v->pcm_returned=v->pcm_current; |
432 | 3.32k | } |
433 | | |
434 | 6.09k | } |
435 | | |
436 | 9.62k | } |
437 | 48.4k | }else{ |
438 | 2.75k | v->granulepos+=ci->blocksizes[v->lW]/4+ci->blocksizes[v->W]/4; |
439 | 2.75k | if(vb->granulepos!=-1 && v->granulepos!=vb->granulepos){ |
440 | | |
441 | 2.20k | if(v->granulepos>vb->granulepos){ |
442 | 1.49k | long extra=v->granulepos-vb->granulepos; |
443 | | |
444 | 1.49k | if(extra) |
445 | 1.49k | if(vb->eofflag){ |
446 | | /* partial last frame. Strip the extra samples off */ |
447 | | |
448 | | /* Guard against corrupt/malicious frames that set EOP and |
449 | | a backdated granpos; don't rewind more samples than we |
450 | | actually have */ |
451 | 994 | if(extra > v->pcm_current - v->pcm_returned) |
452 | 855 | extra = v->pcm_current - v->pcm_returned; |
453 | | |
454 | | /* we use ogg_int64_t for granule positions because a |
455 | | uint64 isn't universally available. Unfortunately, |
456 | | that means granposes can be 'negative' and result in |
457 | | extra being negative */ |
458 | 994 | if(extra<0) |
459 | 133 | extra=0; |
460 | | |
461 | 994 | v->pcm_current-=extra; |
462 | | |
463 | 994 | } /* else {Shouldn't happen *unless* the bitstream is out of |
464 | | spec. Either way, believe the bitstream } */ |
465 | 1.49k | } /* else {Shouldn't happen *unless* the bitstream is out of |
466 | | spec. Either way, believe the bitstream } */ |
467 | 2.20k | v->granulepos=vb->granulepos; |
468 | 2.20k | } |
469 | 2.75k | } |
470 | | |
471 | | /* Update, cleanup */ |
472 | | |
473 | 51.2k | if(vb->eofflag)v->eofflag=1; |
474 | 51.2k | return(0); |
475 | 53.2k | } |
476 | | |
477 | | /* pcm==NULL indicates we just want the pending samples, no more */ |
478 | 1.57M | int vorbis_synthesis_pcmout(vorbis_dsp_state *v,ogg_int32_t ***pcm){ |
479 | 1.57M | vorbis_info *vi=v->vi; |
480 | 1.57M | if(v->pcm_returned>-1 && v->pcm_returned<v->pcm_current){ |
481 | 1.45M | if(pcm){ |
482 | 1.41M | int i; |
483 | 308M | for(i=0;i<vi->channels;i++) |
484 | 306M | v->pcmret[i]=v->pcm[i]+v->pcm_returned; |
485 | 1.41M | *pcm=v->pcmret; |
486 | 1.41M | } |
487 | 1.45M | return(v->pcm_current-v->pcm_returned); |
488 | 1.45M | } |
489 | 119k | return(0); |
490 | 1.57M | } |
491 | | |
492 | 1.41M | int vorbis_synthesis_read(vorbis_dsp_state *v,int bytes){ |
493 | 1.41M | if(bytes && v->pcm_returned+bytes>v->pcm_current)return(OV_EINVAL); |
494 | 1.41M | v->pcm_returned+=bytes; |
495 | 1.41M | return(0); |
496 | 1.41M | } |
497 | | |