media/libopus/src/opus_multistream_decoder.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* Copyright (c) 2011 Xiph.Org Foundation
michael@0 2 Written by Jean-Marc Valin */
michael@0 3 /*
michael@0 4 Redistribution and use in source and binary forms, with or without
michael@0 5 modification, are permitted provided that the following conditions
michael@0 6 are met:
michael@0 7
michael@0 8 - Redistributions of source code must retain the above copyright
michael@0 9 notice, this list of conditions and the following disclaimer.
michael@0 10
michael@0 11 - Redistributions in binary form must reproduce the above copyright
michael@0 12 notice, this list of conditions and the following disclaimer in the
michael@0 13 documentation and/or other materials provided with the distribution.
michael@0 14
michael@0 15 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
michael@0 16 ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
michael@0 17 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
michael@0 18 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
michael@0 19 OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
michael@0 20 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
michael@0 21 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
michael@0 22 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
michael@0 23 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
michael@0 24 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
michael@0 25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
michael@0 26 */
michael@0 27
michael@0 28 #ifdef HAVE_CONFIG_H
michael@0 29 #include "config.h"
michael@0 30 #endif
michael@0 31
michael@0 32 #include "opus_multistream.h"
michael@0 33 #include "opus.h"
michael@0 34 #include "opus_private.h"
michael@0 35 #include "stack_alloc.h"
michael@0 36 #include <stdarg.h>
michael@0 37 #include "float_cast.h"
michael@0 38 #include "os_support.h"
michael@0 39
michael@0 40 struct OpusMSDecoder {
michael@0 41 ChannelLayout layout;
michael@0 42 /* Decoder states go here */
michael@0 43 };
michael@0 44
michael@0 45
michael@0 46
michael@0 47
michael@0 48 /* DECODER */
michael@0 49
michael@0 50 opus_int32 opus_multistream_decoder_get_size(int nb_streams, int nb_coupled_streams)
michael@0 51 {
michael@0 52 int coupled_size;
michael@0 53 int mono_size;
michael@0 54
michael@0 55 if(nb_streams<1||nb_coupled_streams>nb_streams||nb_coupled_streams<0)return 0;
michael@0 56 coupled_size = opus_decoder_get_size(2);
michael@0 57 mono_size = opus_decoder_get_size(1);
michael@0 58 return align(sizeof(OpusMSDecoder))
michael@0 59 + nb_coupled_streams * align(coupled_size)
michael@0 60 + (nb_streams-nb_coupled_streams) * align(mono_size);
michael@0 61 }
michael@0 62
michael@0 63 int opus_multistream_decoder_init(
michael@0 64 OpusMSDecoder *st,
michael@0 65 opus_int32 Fs,
michael@0 66 int channels,
michael@0 67 int streams,
michael@0 68 int coupled_streams,
michael@0 69 const unsigned char *mapping
michael@0 70 )
michael@0 71 {
michael@0 72 int coupled_size;
michael@0 73 int mono_size;
michael@0 74 int i, ret;
michael@0 75 char *ptr;
michael@0 76
michael@0 77 if ((channels>255) || (channels<1) || (coupled_streams>streams) ||
michael@0 78 (coupled_streams+streams>255) || (streams<1) || (coupled_streams<0))
michael@0 79 return OPUS_BAD_ARG;
michael@0 80
michael@0 81 st->layout.nb_channels = channels;
michael@0 82 st->layout.nb_streams = streams;
michael@0 83 st->layout.nb_coupled_streams = coupled_streams;
michael@0 84
michael@0 85 for (i=0;i<st->layout.nb_channels;i++)
michael@0 86 st->layout.mapping[i] = mapping[i];
michael@0 87 if (!validate_layout(&st->layout))
michael@0 88 return OPUS_BAD_ARG;
michael@0 89
michael@0 90 ptr = (char*)st + align(sizeof(OpusMSDecoder));
michael@0 91 coupled_size = opus_decoder_get_size(2);
michael@0 92 mono_size = opus_decoder_get_size(1);
michael@0 93
michael@0 94 for (i=0;i<st->layout.nb_coupled_streams;i++)
michael@0 95 {
michael@0 96 ret=opus_decoder_init((OpusDecoder*)ptr, Fs, 2);
michael@0 97 if(ret!=OPUS_OK)return ret;
michael@0 98 ptr += align(coupled_size);
michael@0 99 }
michael@0 100 for (;i<st->layout.nb_streams;i++)
michael@0 101 {
michael@0 102 ret=opus_decoder_init((OpusDecoder*)ptr, Fs, 1);
michael@0 103 if(ret!=OPUS_OK)return ret;
michael@0 104 ptr += align(mono_size);
michael@0 105 }
michael@0 106 return OPUS_OK;
michael@0 107 }
michael@0 108
michael@0 109
michael@0 110 OpusMSDecoder *opus_multistream_decoder_create(
michael@0 111 opus_int32 Fs,
michael@0 112 int channels,
michael@0 113 int streams,
michael@0 114 int coupled_streams,
michael@0 115 const unsigned char *mapping,
michael@0 116 int *error
michael@0 117 )
michael@0 118 {
michael@0 119 int ret;
michael@0 120 OpusMSDecoder *st;
michael@0 121 if ((channels>255) || (channels<1) || (coupled_streams>streams) ||
michael@0 122 (coupled_streams+streams>255) || (streams<1) || (coupled_streams<0))
michael@0 123 {
michael@0 124 if (error)
michael@0 125 *error = OPUS_BAD_ARG;
michael@0 126 return NULL;
michael@0 127 }
michael@0 128 st = (OpusMSDecoder *)opus_alloc(opus_multistream_decoder_get_size(streams, coupled_streams));
michael@0 129 if (st==NULL)
michael@0 130 {
michael@0 131 if (error)
michael@0 132 *error = OPUS_ALLOC_FAIL;
michael@0 133 return NULL;
michael@0 134 }
michael@0 135 ret = opus_multistream_decoder_init(st, Fs, channels, streams, coupled_streams, mapping);
michael@0 136 if (error)
michael@0 137 *error = ret;
michael@0 138 if (ret != OPUS_OK)
michael@0 139 {
michael@0 140 opus_free(st);
michael@0 141 st = NULL;
michael@0 142 }
michael@0 143 return st;
michael@0 144 }
michael@0 145
michael@0 146 typedef void (*opus_copy_channel_out_func)(
michael@0 147 void *dst,
michael@0 148 int dst_stride,
michael@0 149 int dst_channel,
michael@0 150 const opus_val16 *src,
michael@0 151 int src_stride,
michael@0 152 int frame_size
michael@0 153 );
michael@0 154
michael@0 155 static int opus_multistream_packet_validate(const unsigned char *data,
michael@0 156 opus_int32 len, int nb_streams, opus_int32 Fs)
michael@0 157 {
michael@0 158 int s;
michael@0 159 int count;
michael@0 160 unsigned char toc;
michael@0 161 opus_int16 size[48];
michael@0 162 int samples=0;
michael@0 163 opus_int32 packet_offset;
michael@0 164
michael@0 165 for (s=0;s<nb_streams;s++)
michael@0 166 {
michael@0 167 int tmp_samples;
michael@0 168 if (len<=0)
michael@0 169 return OPUS_INVALID_PACKET;
michael@0 170 count = opus_packet_parse_impl(data, len, s!=nb_streams-1, &toc, NULL,
michael@0 171 size, NULL, &packet_offset);
michael@0 172 if (count<0)
michael@0 173 return count;
michael@0 174 tmp_samples = opus_packet_get_nb_samples(data, packet_offset, Fs);
michael@0 175 if (s!=0 && samples != tmp_samples)
michael@0 176 return OPUS_INVALID_PACKET;
michael@0 177 samples = tmp_samples;
michael@0 178 data += packet_offset;
michael@0 179 len -= packet_offset;
michael@0 180 }
michael@0 181 return samples;
michael@0 182 }
michael@0 183
michael@0 184 static int opus_multistream_decode_native(
michael@0 185 OpusMSDecoder *st,
michael@0 186 const unsigned char *data,
michael@0 187 opus_int32 len,
michael@0 188 void *pcm,
michael@0 189 opus_copy_channel_out_func copy_channel_out,
michael@0 190 int frame_size,
michael@0 191 int decode_fec,
michael@0 192 int soft_clip
michael@0 193 )
michael@0 194 {
michael@0 195 opus_int32 Fs;
michael@0 196 int coupled_size;
michael@0 197 int mono_size;
michael@0 198 int s, c;
michael@0 199 char *ptr;
michael@0 200 int do_plc=0;
michael@0 201 VARDECL(opus_val16, buf);
michael@0 202 ALLOC_STACK;
michael@0 203
michael@0 204 /* Limit frame_size to avoid excessive stack allocations. */
michael@0 205 opus_multistream_decoder_ctl(st, OPUS_GET_SAMPLE_RATE(&Fs));
michael@0 206 frame_size = IMIN(frame_size, Fs/25*3);
michael@0 207 ALLOC(buf, 2*frame_size, opus_val16);
michael@0 208 ptr = (char*)st + align(sizeof(OpusMSDecoder));
michael@0 209 coupled_size = opus_decoder_get_size(2);
michael@0 210 mono_size = opus_decoder_get_size(1);
michael@0 211
michael@0 212 if (len==0)
michael@0 213 do_plc = 1;
michael@0 214 if (len < 0)
michael@0 215 {
michael@0 216 RESTORE_STACK;
michael@0 217 return OPUS_BAD_ARG;
michael@0 218 }
michael@0 219 if (!do_plc && len < 2*st->layout.nb_streams-1)
michael@0 220 {
michael@0 221 RESTORE_STACK;
michael@0 222 return OPUS_INVALID_PACKET;
michael@0 223 }
michael@0 224 if (!do_plc)
michael@0 225 {
michael@0 226 int ret = opus_multistream_packet_validate(data, len, st->layout.nb_streams, Fs);
michael@0 227 if (ret < 0)
michael@0 228 {
michael@0 229 RESTORE_STACK;
michael@0 230 return ret;
michael@0 231 } else if (ret > frame_size)
michael@0 232 {
michael@0 233 RESTORE_STACK;
michael@0 234 return OPUS_BUFFER_TOO_SMALL;
michael@0 235 }
michael@0 236 }
michael@0 237 for (s=0;s<st->layout.nb_streams;s++)
michael@0 238 {
michael@0 239 OpusDecoder *dec;
michael@0 240 int packet_offset, ret;
michael@0 241
michael@0 242 dec = (OpusDecoder*)ptr;
michael@0 243 ptr += (s < st->layout.nb_coupled_streams) ? align(coupled_size) : align(mono_size);
michael@0 244
michael@0 245 if (!do_plc && len<=0)
michael@0 246 {
michael@0 247 RESTORE_STACK;
michael@0 248 return OPUS_INTERNAL_ERROR;
michael@0 249 }
michael@0 250 packet_offset = 0;
michael@0 251 ret = opus_decode_native(dec, data, len, buf, frame_size, decode_fec, s!=st->layout.nb_streams-1, &packet_offset, soft_clip);
michael@0 252 data += packet_offset;
michael@0 253 len -= packet_offset;
michael@0 254 if (ret <= 0)
michael@0 255 {
michael@0 256 RESTORE_STACK;
michael@0 257 return ret;
michael@0 258 }
michael@0 259 frame_size = ret;
michael@0 260 if (s < st->layout.nb_coupled_streams)
michael@0 261 {
michael@0 262 int chan, prev;
michael@0 263 prev = -1;
michael@0 264 /* Copy "left" audio to the channel(s) where it belongs */
michael@0 265 while ( (chan = get_left_channel(&st->layout, s, prev)) != -1)
michael@0 266 {
michael@0 267 (*copy_channel_out)(pcm, st->layout.nb_channels, chan,
michael@0 268 buf, 2, frame_size);
michael@0 269 prev = chan;
michael@0 270 }
michael@0 271 prev = -1;
michael@0 272 /* Copy "right" audio to the channel(s) where it belongs */
michael@0 273 while ( (chan = get_right_channel(&st->layout, s, prev)) != -1)
michael@0 274 {
michael@0 275 (*copy_channel_out)(pcm, st->layout.nb_channels, chan,
michael@0 276 buf+1, 2, frame_size);
michael@0 277 prev = chan;
michael@0 278 }
michael@0 279 } else {
michael@0 280 int chan, prev;
michael@0 281 prev = -1;
michael@0 282 /* Copy audio to the channel(s) where it belongs */
michael@0 283 while ( (chan = get_mono_channel(&st->layout, s, prev)) != -1)
michael@0 284 {
michael@0 285 (*copy_channel_out)(pcm, st->layout.nb_channels, chan,
michael@0 286 buf, 1, frame_size);
michael@0 287 prev = chan;
michael@0 288 }
michael@0 289 }
michael@0 290 }
michael@0 291 /* Handle muted channels */
michael@0 292 for (c=0;c<st->layout.nb_channels;c++)
michael@0 293 {
michael@0 294 if (st->layout.mapping[c] == 255)
michael@0 295 {
michael@0 296 (*copy_channel_out)(pcm, st->layout.nb_channels, c,
michael@0 297 NULL, 0, frame_size);
michael@0 298 }
michael@0 299 }
michael@0 300 RESTORE_STACK;
michael@0 301 return frame_size;
michael@0 302 }
michael@0 303
michael@0 304 #if !defined(DISABLE_FLOAT_API)
michael@0 305 static void opus_copy_channel_out_float(
michael@0 306 void *dst,
michael@0 307 int dst_stride,
michael@0 308 int dst_channel,
michael@0 309 const opus_val16 *src,
michael@0 310 int src_stride,
michael@0 311 int frame_size
michael@0 312 )
michael@0 313 {
michael@0 314 float *float_dst;
michael@0 315 opus_int32 i;
michael@0 316 float_dst = (float*)dst;
michael@0 317 if (src != NULL)
michael@0 318 {
michael@0 319 for (i=0;i<frame_size;i++)
michael@0 320 #if defined(FIXED_POINT)
michael@0 321 float_dst[i*dst_stride+dst_channel] = (1/32768.f)*src[i*src_stride];
michael@0 322 #else
michael@0 323 float_dst[i*dst_stride+dst_channel] = src[i*src_stride];
michael@0 324 #endif
michael@0 325 }
michael@0 326 else
michael@0 327 {
michael@0 328 for (i=0;i<frame_size;i++)
michael@0 329 float_dst[i*dst_stride+dst_channel] = 0;
michael@0 330 }
michael@0 331 }
michael@0 332 #endif
michael@0 333
michael@0 334 static void opus_copy_channel_out_short(
michael@0 335 void *dst,
michael@0 336 int dst_stride,
michael@0 337 int dst_channel,
michael@0 338 const opus_val16 *src,
michael@0 339 int src_stride,
michael@0 340 int frame_size
michael@0 341 )
michael@0 342 {
michael@0 343 opus_int16 *short_dst;
michael@0 344 opus_int32 i;
michael@0 345 short_dst = (opus_int16*)dst;
michael@0 346 if (src != NULL)
michael@0 347 {
michael@0 348 for (i=0;i<frame_size;i++)
michael@0 349 #if defined(FIXED_POINT)
michael@0 350 short_dst[i*dst_stride+dst_channel] = src[i*src_stride];
michael@0 351 #else
michael@0 352 short_dst[i*dst_stride+dst_channel] = FLOAT2INT16(src[i*src_stride]);
michael@0 353 #endif
michael@0 354 }
michael@0 355 else
michael@0 356 {
michael@0 357 for (i=0;i<frame_size;i++)
michael@0 358 short_dst[i*dst_stride+dst_channel] = 0;
michael@0 359 }
michael@0 360 }
michael@0 361
michael@0 362
michael@0 363
michael@0 364 #ifdef FIXED_POINT
michael@0 365 int opus_multistream_decode(
michael@0 366 OpusMSDecoder *st,
michael@0 367 const unsigned char *data,
michael@0 368 opus_int32 len,
michael@0 369 opus_int16 *pcm,
michael@0 370 int frame_size,
michael@0 371 int decode_fec
michael@0 372 )
michael@0 373 {
michael@0 374 return opus_multistream_decode_native(st, data, len,
michael@0 375 pcm, opus_copy_channel_out_short, frame_size, decode_fec, 0);
michael@0 376 }
michael@0 377
michael@0 378 #ifndef DISABLE_FLOAT_API
michael@0 379 int opus_multistream_decode_float(OpusMSDecoder *st, const unsigned char *data,
michael@0 380 opus_int32 len, float *pcm, int frame_size, int decode_fec)
michael@0 381 {
michael@0 382 return opus_multistream_decode_native(st, data, len,
michael@0 383 pcm, opus_copy_channel_out_float, frame_size, decode_fec, 0);
michael@0 384 }
michael@0 385 #endif
michael@0 386
michael@0 387 #else
michael@0 388
michael@0 389 int opus_multistream_decode(OpusMSDecoder *st, const unsigned char *data,
michael@0 390 opus_int32 len, opus_int16 *pcm, int frame_size, int decode_fec)
michael@0 391 {
michael@0 392 return opus_multistream_decode_native(st, data, len,
michael@0 393 pcm, opus_copy_channel_out_short, frame_size, decode_fec, 1);
michael@0 394 }
michael@0 395
michael@0 396 int opus_multistream_decode_float(
michael@0 397 OpusMSDecoder *st,
michael@0 398 const unsigned char *data,
michael@0 399 opus_int32 len,
michael@0 400 float *pcm,
michael@0 401 int frame_size,
michael@0 402 int decode_fec
michael@0 403 )
michael@0 404 {
michael@0 405 return opus_multistream_decode_native(st, data, len,
michael@0 406 pcm, opus_copy_channel_out_float, frame_size, decode_fec, 0);
michael@0 407 }
michael@0 408 #endif
michael@0 409
michael@0 410 int opus_multistream_decoder_ctl(OpusMSDecoder *st, int request, ...)
michael@0 411 {
michael@0 412 va_list ap;
michael@0 413 int coupled_size, mono_size;
michael@0 414 char *ptr;
michael@0 415 int ret = OPUS_OK;
michael@0 416
michael@0 417 va_start(ap, request);
michael@0 418
michael@0 419 coupled_size = opus_decoder_get_size(2);
michael@0 420 mono_size = opus_decoder_get_size(1);
michael@0 421 ptr = (char*)st + align(sizeof(OpusMSDecoder));
michael@0 422 switch (request)
michael@0 423 {
michael@0 424 case OPUS_GET_BANDWIDTH_REQUEST:
michael@0 425 case OPUS_GET_SAMPLE_RATE_REQUEST:
michael@0 426 case OPUS_GET_GAIN_REQUEST:
michael@0 427 case OPUS_GET_LAST_PACKET_DURATION_REQUEST:
michael@0 428 {
michael@0 429 OpusDecoder *dec;
michael@0 430 /* For int32* GET params, just query the first stream */
michael@0 431 opus_int32 *value = va_arg(ap, opus_int32*);
michael@0 432 dec = (OpusDecoder*)ptr;
michael@0 433 ret = opus_decoder_ctl(dec, request, value);
michael@0 434 }
michael@0 435 break;
michael@0 436 case OPUS_GET_FINAL_RANGE_REQUEST:
michael@0 437 {
michael@0 438 int s;
michael@0 439 opus_uint32 *value = va_arg(ap, opus_uint32*);
michael@0 440 opus_uint32 tmp;
michael@0 441 if (!value)
michael@0 442 {
michael@0 443 goto bad_arg;
michael@0 444 }
michael@0 445 *value = 0;
michael@0 446 for (s=0;s<st->layout.nb_streams;s++)
michael@0 447 {
michael@0 448 OpusDecoder *dec;
michael@0 449 dec = (OpusDecoder*)ptr;
michael@0 450 if (s < st->layout.nb_coupled_streams)
michael@0 451 ptr += align(coupled_size);
michael@0 452 else
michael@0 453 ptr += align(mono_size);
michael@0 454 ret = opus_decoder_ctl(dec, request, &tmp);
michael@0 455 if (ret != OPUS_OK) break;
michael@0 456 *value ^= tmp;
michael@0 457 }
michael@0 458 }
michael@0 459 break;
michael@0 460 case OPUS_RESET_STATE:
michael@0 461 {
michael@0 462 int s;
michael@0 463 for (s=0;s<st->layout.nb_streams;s++)
michael@0 464 {
michael@0 465 OpusDecoder *dec;
michael@0 466
michael@0 467 dec = (OpusDecoder*)ptr;
michael@0 468 if (s < st->layout.nb_coupled_streams)
michael@0 469 ptr += align(coupled_size);
michael@0 470 else
michael@0 471 ptr += align(mono_size);
michael@0 472 ret = opus_decoder_ctl(dec, OPUS_RESET_STATE);
michael@0 473 if (ret != OPUS_OK)
michael@0 474 break;
michael@0 475 }
michael@0 476 }
michael@0 477 break;
michael@0 478 case OPUS_MULTISTREAM_GET_DECODER_STATE_REQUEST:
michael@0 479 {
michael@0 480 int s;
michael@0 481 opus_int32 stream_id;
michael@0 482 OpusDecoder **value;
michael@0 483 stream_id = va_arg(ap, opus_int32);
michael@0 484 if (stream_id<0 || stream_id >= st->layout.nb_streams)
michael@0 485 ret = OPUS_BAD_ARG;
michael@0 486 value = va_arg(ap, OpusDecoder**);
michael@0 487 if (!value)
michael@0 488 {
michael@0 489 goto bad_arg;
michael@0 490 }
michael@0 491 for (s=0;s<stream_id;s++)
michael@0 492 {
michael@0 493 if (s < st->layout.nb_coupled_streams)
michael@0 494 ptr += align(coupled_size);
michael@0 495 else
michael@0 496 ptr += align(mono_size);
michael@0 497 }
michael@0 498 *value = (OpusDecoder*)ptr;
michael@0 499 }
michael@0 500 break;
michael@0 501 case OPUS_SET_GAIN_REQUEST:
michael@0 502 {
michael@0 503 int s;
michael@0 504 /* This works for int32 params */
michael@0 505 opus_int32 value = va_arg(ap, opus_int32);
michael@0 506 for (s=0;s<st->layout.nb_streams;s++)
michael@0 507 {
michael@0 508 OpusDecoder *dec;
michael@0 509
michael@0 510 dec = (OpusDecoder*)ptr;
michael@0 511 if (s < st->layout.nb_coupled_streams)
michael@0 512 ptr += align(coupled_size);
michael@0 513 else
michael@0 514 ptr += align(mono_size);
michael@0 515 ret = opus_decoder_ctl(dec, request, value);
michael@0 516 if (ret != OPUS_OK)
michael@0 517 break;
michael@0 518 }
michael@0 519 }
michael@0 520 break;
michael@0 521 default:
michael@0 522 ret = OPUS_UNIMPLEMENTED;
michael@0 523 break;
michael@0 524 }
michael@0 525
michael@0 526 va_end(ap);
michael@0 527 return ret;
michael@0 528 bad_arg:
michael@0 529 va_end(ap);
michael@0 530 return OPUS_BAD_ARG;
michael@0 531 }
michael@0 532
michael@0 533
michael@0 534 void opus_multistream_decoder_destroy(OpusMSDecoder *st)
michael@0 535 {
michael@0 536 opus_free(st);
michael@0 537 }

mercurial