Skip to content

AFNI/NIfTI Server

Sections
Personal tools
You are here: Home » AFNI » Documentation

Doxygen Source Code Documentation


Main Page   Alphabetical List   Data Structures   File List   Data Fields   Globals   Search  

mpeg2dec.c

Go to the documentation of this file.
00001 /*
00002  * mpeg2dec.c
00003  * Copyright (C) 2000-2002 Michel Lespinasse <walken@zoy.org>
00004  * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
00005  *
00006  * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
00007  * See http://libmpeg2.sourceforge.net/ for updates.
00008  *
00009  * mpeg2dec is free software; you can redistribute it and/or modify
00010  * it under the terms of the GNU General Public License as published by
00011  * the Free Software Foundation; either version 2 of the License, or
00012  * (at your option) any later version.
00013  *
00014  * mpeg2dec is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  * GNU General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU General Public License
00020  * along with this program; if not, write to the Free Software
00021  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022  */
00023 
00024 #include "config.h"
00025 
00026 #include <stdio.h>
00027 #include <stdlib.h>
00028 #include <string.h>
00029 #include <errno.h>
00030 #include <signal.h>
00031 #include <getopt.h>
00032 #ifdef HAVE_IO_H
00033 #include <fcntl.h>
00034 #include <io.h>
00035 #endif
00036 #include <inttypes.h>
00037 
00038 #include "mpeg2.h"
00039 #include "video_out.h"
00040 #include "convert.h"
00041 #include "gettimeofday.h"
00042 
00043 #define BUFFER_SIZE 4096
00044 static uint8_t buffer[BUFFER_SIZE];
00045 static FILE * in_file;
00046 static int demux_track = 0;
00047 static int demux_pid = 0;
00048 static int demux_pva = 0;
00049 static mpeg2dec_t * mpeg2dec;
00050 static vo_open_t * output_open = NULL;
00051 static vo_instance_t * output;
00052 
00053 #ifdef HAVE_GETTIMEOFDAY
00054 
00055 static void print_fps (int final);
00056 
00057 static RETSIGTYPE signal_handler (int sig)
00058 {
00059     print_fps (1);
00060     signal (sig, SIG_DFL);
00061     raise (sig);
00062 }
00063 
00064 static void print_fps (int final)
00065 {
00066     static uint32_t frame_counter = 0;
00067     static struct timeval tv_beg, tv_start;
00068     static int total_elapsed;
00069     static int last_count = 0;
00070     struct timeval tv_end;
00071     float fps, tfps;
00072     int frames, elapsed;
00073 
00074     gettimeofday (&tv_end, NULL);
00075 
00076     if (!frame_counter) {
00077         tv_start = tv_beg = tv_end;
00078         signal (SIGINT, signal_handler);
00079     }
00080 
00081     elapsed = (tv_end.tv_sec - tv_beg.tv_sec) * 100 +
00082         (tv_end.tv_usec - tv_beg.tv_usec) / 10000;
00083     total_elapsed = (tv_end.tv_sec - tv_start.tv_sec) * 100 +
00084         (tv_end.tv_usec - tv_start.tv_usec) / 10000;
00085 
00086     if (final) {
00087         if (total_elapsed)
00088             tfps = frame_counter * 100.0 / total_elapsed;
00089         else
00090             tfps = 0;
00091 
00092         fprintf (stderr,"\n%d frames decoded in %.2f seconds (%.2f fps)\n",
00093                  frame_counter, total_elapsed / 100.0, tfps);
00094 
00095         return;
00096     }
00097 
00098     frame_counter++;
00099 
00100     if (elapsed < 50)   /* only display every 0.50 seconds */
00101         return;
00102 
00103     tv_beg = tv_end;
00104     frames = frame_counter - last_count;
00105 
00106     fps = frames * 100.0 / elapsed;
00107     tfps = frame_counter * 100.0 / total_elapsed;
00108 
00109     fprintf (stderr, "%d frames in %.2f sec (%.2f fps), "
00110              "%d last %.2f sec (%.2f fps)\033[K\r", frame_counter,
00111              total_elapsed / 100.0, tfps, frames, elapsed / 100.0, fps);
00112 
00113     last_count = frame_counter;
00114 }
00115 
00116 #else /* !HAVE_GETTIMEOFDAY */
00117 
00118 static void print_fps (int final)
00119 {
00120 }
00121 
00122 #endif
00123 
00124 static void print_usage (char ** argv)
00125 {
00126     int i;
00127     vo_driver_t * drivers;
00128 
00129     fprintf (stderr, "usage: %s [-o <mode>] [-s [<track>]] [-t <pid>] [-p] "
00130              "[-c] <file>\n"
00131              "\t-s\tuse program stream demultiplexer, "
00132              "track 0-15 or 0xe0-0xef\n"
00133              "\t-t\tuse transport stream demultiplexer, pid 0x10-0x1ffe\n"
00134              "\t-p\tuse pva demultiplexer\n"
00135              "\t-c\tuse c implementation, disables all accelerations\n"
00136              "\t-o\tvideo output mode\n", argv[0]);
00137 
00138     drivers = vo_drivers ();
00139     for (i = 0; drivers[i].name; i++)
00140         fprintf (stderr, "\t\t\t%s\n", drivers[i].name);
00141 
00142     exit (1);
00143 }
00144 
00145 static void handle_args (int argc, char ** argv)
00146 {
00147     int c;
00148     vo_driver_t * drivers;
00149     int i;
00150     char * s;
00151 
00152     drivers = vo_drivers ();
00153     while ((c = getopt (argc, argv, "s::t:pco:")) != -1)
00154         switch (c) {
00155         case 'o':
00156             for (i = 0; drivers[i].name != NULL; i++)
00157                 if (strcmp (drivers[i].name, optarg) == 0)
00158                     output_open = drivers[i].open;
00159             if (output_open == NULL) {
00160                 fprintf (stderr, "Invalid video driver: %s\n", optarg);
00161                 print_usage (argv);
00162             }
00163             break;
00164 
00165         case 's':
00166             demux_track = 0xe0;
00167             if (optarg != NULL) {
00168                 demux_track = strtol (optarg, &s, 0);
00169                 if (demux_track < 0xe0)
00170                     demux_track += 0xe0;
00171                 if ((demux_track < 0xe0) || (demux_track > 0xef) || (*s)) {
00172                     fprintf (stderr, "Invalid track number: %s\n", optarg);
00173                     print_usage (argv);
00174                 }
00175             }
00176             break;
00177 
00178         case 't':
00179             demux_pid = strtol (optarg, &s, 0);
00180             if ((demux_pid < 0x10) || (demux_pid > 0x1ffe) || (*s)) {
00181                 fprintf (stderr, "Invalid pid: %s\n", optarg);
00182                 print_usage (argv);
00183             }
00184             break;
00185 
00186         case 'p':
00187             demux_pva = 1;
00188             break;
00189 
00190         case 'c':
00191             mpeg2_accel (0);
00192             break;
00193 
00194         default:
00195             print_usage (argv);
00196         }
00197 
00198     /* -o not specified, use a default driver */
00199     if (output_open == NULL)
00200         output_open = drivers[0].open;
00201 
00202     if (optind < argc) {
00203         in_file = fopen (argv[optind], "rb");
00204         if (!in_file) {
00205             fprintf (stderr, "%s - could not open file %s\n", strerror (errno),
00206                      argv[optind]);
00207             exit (1);
00208         }
00209     } else
00210         in_file = stdin;
00211 }
00212 
00213 static void decode_mpeg2 (uint8_t * current, uint8_t * end)
00214 {
00215     const mpeg2_info_t * info;
00216     int state;
00217     vo_setup_result_t setup_result;
00218 
00219     mpeg2_buffer (mpeg2dec, current, end);
00220 
00221     info = mpeg2_info (mpeg2dec);
00222     while (1) {
00223         state = mpeg2_parse (mpeg2dec);
00224         switch (state) {
00225         case -1:
00226             return;
00227         case STATE_SEQUENCE:
00228             /* might set nb fbuf, convert format, stride */
00229             /* might set fbufs */
00230             if (output->setup (output, info->sequence->width,
00231                                info->sequence->height, &setup_result)) {
00232                 fprintf (stderr, "display setup failed\n");
00233                 exit (1);
00234             }
00235             if (setup_result.convert)
00236                 mpeg2_convert (mpeg2dec, setup_result.convert, NULL);
00237             if (output->set_fbuf) {
00238                 uint8_t * buf[3];
00239                 void * id;
00240 
00241                 mpeg2_custom_fbuf (mpeg2dec, 1);
00242                 output->set_fbuf (output, buf, &id);
00243                 mpeg2_set_buf (mpeg2dec, buf, id);
00244                 output->set_fbuf (output, buf, &id);
00245                 mpeg2_set_buf (mpeg2dec, buf, id);
00246             } else if (output->setup_fbuf) {
00247                 uint8_t * buf[3];
00248                 void * id;
00249 
00250                 output->setup_fbuf (output, buf, &id);
00251                 mpeg2_set_buf (mpeg2dec, buf, id);
00252                 output->setup_fbuf (output, buf, &id);
00253                 mpeg2_set_buf (mpeg2dec, buf, id);
00254                 output->setup_fbuf (output, buf, &id);
00255                 mpeg2_set_buf (mpeg2dec, buf, id);
00256             }
00257             break;
00258         case STATE_PICTURE:
00259             /* might skip */
00260             /* might set fbuf */
00261             if (output->set_fbuf) {
00262                 uint8_t * buf[3];
00263                 void * id;
00264 
00265                 output->set_fbuf (output, buf, &id);
00266                 mpeg2_set_buf (mpeg2dec, buf, id);
00267             }
00268             if (output->start_fbuf)
00269                 output->start_fbuf (output, info->current_fbuf->buf,
00270                                     info->current_fbuf->id);
00271             break;
00272         case STATE_PICTURE_2ND:
00273             /* should not do anything */
00274             break;
00275         case STATE_SLICE:
00276         case STATE_END:
00277             /* draw current picture */
00278             /* might free frame buffer */
00279             if (info->display_fbuf) {
00280                 output->draw (output, info->display_fbuf->buf,
00281                               info->display_fbuf->id);
00282                 print_fps (0);
00283             }
00284             if (output->discard && info->discard_fbuf)
00285                 output->discard (output, info->discard_fbuf->buf,
00286                                  info->discard_fbuf->id);
00287             break;
00288         }
00289     }
00290 }
00291 
00292 #define DEMUX_PAYLOAD_START 1
00293 static int demux (uint8_t * buf, uint8_t * end, int flags)
00294 {
00295     static int mpeg1_skip_table[16] = {
00296         0, 0, 4, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
00297     };
00298 
00299     /*
00300      * the demuxer keeps some state between calls:
00301      * if "state" = DEMUX_HEADER, then "head_buf" contains the first
00302      *     "bytes" bytes from some header.
00303      * if "state" == DEMUX_DATA, then we need to copy "bytes" bytes
00304      *     of ES data before the next header.
00305      * if "state" == DEMUX_SKIP, then we need to skip "bytes" bytes
00306      *     of data before the next header.
00307      *
00308      * NEEDBYTES makes sure we have the requested number of bytes for a
00309      * header. If we dont, it copies what we have into head_buf and returns,
00310      * so that when we come back with more data we finish decoding this header.
00311      *
00312      * DONEBYTES updates "buf" to point after the header we just parsed.
00313      */
00314 
00315 #define DEMUX_HEADER 0
00316 #define DEMUX_DATA 1
00317 #define DEMUX_SKIP 2
00318     static int state = DEMUX_SKIP;
00319     static int state_bytes = 0;
00320     static uint8_t head_buf[264];
00321 
00322     uint8_t * header;
00323     int bytes;
00324     int len;
00325 
00326 #define NEEDBYTES(x)                                            \
00327     do {                                                        \
00328         int missing;                                            \
00329                                                                 \
00330         missing = (x) - bytes;                                  \
00331         if (missing > 0) {                                      \
00332             if (header == head_buf) {                           \
00333                 if (missing <= end - buf) {                     \
00334                     memcpy (header + bytes, buf, missing);      \
00335                     buf += missing;                             \
00336                     bytes = (x);                                \
00337                 } else {                                        \
00338                     memcpy (header + bytes, buf, end - buf);    \
00339                     state_bytes = bytes + end - buf;            \
00340                     return 0;                                   \
00341                 }                                               \
00342             } else {                                            \
00343                 memcpy (head_buf, header, bytes);               \
00344                 state = DEMUX_HEADER;                           \
00345                 state_bytes = bytes;                            \
00346                 return 0;                                       \
00347             }                                                   \
00348         }                                                       \
00349     } while (0)
00350 
00351 #define DONEBYTES(x)            \
00352     do {                        \
00353         if (header != head_buf) \
00354             buf = header + (x); \
00355     } while (0)
00356 
00357     if (flags & DEMUX_PAYLOAD_START)
00358         goto payload_start;
00359     switch (state) {
00360     case DEMUX_HEADER:
00361         if (state_bytes > 0) {
00362             header = head_buf;
00363             bytes = state_bytes;
00364             goto continue_header;
00365         }
00366         break;
00367     case DEMUX_DATA:
00368         if (demux_pid || (state_bytes > end - buf)) {
00369             decode_mpeg2 (buf, end);
00370             state_bytes -= end - buf;
00371             return 0;
00372         }
00373         decode_mpeg2 (buf, buf + state_bytes);
00374         buf += state_bytes;
00375         break;
00376     case DEMUX_SKIP:
00377         if (demux_pid || (state_bytes > end - buf)) {
00378             state_bytes -= end - buf;
00379             return 0;
00380         }
00381         buf += state_bytes;
00382         break;
00383     }
00384 
00385     while (1) {
00386         if (demux_pid) {
00387             state = DEMUX_SKIP;
00388             return 0;
00389         }
00390     payload_start:
00391         header = buf;
00392         bytes = end - buf;
00393     continue_header:
00394         NEEDBYTES (4);
00395         if (header[0] || header[1] || (header[2] != 1)) {
00396             if (demux_pid) {
00397                 state = DEMUX_SKIP;
00398                 return 0;
00399             } else if (header != head_buf) {
00400                 buf++;
00401                 goto payload_start;
00402             } else {
00403                 header[0] = header[1];
00404                 header[1] = header[2];
00405                 header[2] = header[3];
00406                 bytes = 3;
00407                 goto continue_header;
00408             }
00409         }
00410         if (demux_pid) {
00411             if ((header[3] >= 0xe0) && (header[3] <= 0xef))
00412                 goto pes;
00413             fprintf (stderr, "bad stream id %x\n", header[3]);
00414             exit (1);
00415         }
00416         switch (header[3]) {
00417         case 0xb9:      /* program end code */
00418             /* DONEBYTES (4); */
00419             /* break;         */
00420             return 1;
00421         case 0xba:      /* pack header */
00422             NEEDBYTES (12);
00423             if ((header[4] & 0xc0) == 0x40) {   /* mpeg2 */
00424                 NEEDBYTES (14);
00425                 len = 14 + (header[13] & 7);
00426                 NEEDBYTES (len);
00427                 DONEBYTES (len);
00428                 /* header points to the mpeg2 pack header */
00429             } else if ((header[4] & 0xf0) == 0x20) {    /* mpeg1 */
00430                 DONEBYTES (12);
00431                 /* header points to the mpeg1 pack header */
00432             } else {
00433                 fprintf (stderr, "weird pack header\n");
00434                 exit (1);
00435             }
00436             break;
00437         default:
00438             if (header[3] == demux_track) {
00439             pes:
00440                 NEEDBYTES (7);
00441                 if ((header[6] & 0xc0) == 0x80) {       /* mpeg2 */
00442                     NEEDBYTES (9);
00443                     len = 9 + header[8];
00444                     NEEDBYTES (len);
00445                     /* header points to the mpeg2 pes header */
00446                     if (header[7] & 0x80) {
00447                         uint32_t pts;
00448 
00449                         pts = (((buf[9] >> 1) << 30) |
00450                                (buf[10] << 22) | ((buf[11] >> 1) << 15) |
00451                                (buf[12] << 7) | (buf[13] >> 1));
00452                         mpeg2_pts (mpeg2dec, pts);
00453                     }
00454                 } else {        /* mpeg1 */
00455                     int len_skip;
00456                     uint8_t * ptsbuf;
00457 
00458                     len = 7;
00459                     while (header[len - 1] == 0xff) {
00460                         len++;
00461                         NEEDBYTES (len);
00462                         if (len > 23) {
00463                             fprintf (stderr, "too much stuffing\n");
00464                             break;
00465                         }
00466                     }
00467                     if ((header[len - 1] & 0xc0) == 0x40) {
00468                         len += 2;
00469                         NEEDBYTES (len);
00470                     }
00471                     len_skip = len;
00472                     len += mpeg1_skip_table[header[len - 1] >> 4];
00473                     NEEDBYTES (len);
00474                     /* header points to the mpeg1 pes header */
00475                     ptsbuf = header + len_skip;
00476                     if (ptsbuf[-1] & 0x20) {
00477                         uint32_t pts;
00478 
00479                         pts = (((ptsbuf[-1] >> 1) << 30) |
00480                                (ptsbuf[0] << 22) | ((ptsbuf[1] >> 1) << 15) |
00481                                (ptsbuf[2] << 7) | (ptsbuf[3] >> 1));
00482                         mpeg2_pts (mpeg2dec, pts);
00483                     }
00484                 }
00485                 DONEBYTES (len);
00486                 bytes = 6 + (header[4] << 8) + header[5] - len;
00487                 if (demux_pid || (bytes > end - buf)) {
00488                     decode_mpeg2 (buf, end);
00489                     state = DEMUX_DATA;
00490                     state_bytes = bytes - (end - buf);
00491                     return 0;
00492                 } else if (bytes > 0) {
00493                     decode_mpeg2 (buf, buf + bytes);
00494                     buf += bytes;
00495                 }
00496             } else if (header[3] < 0xb9) {
00497                 fprintf (stderr,
00498                          "looks like a video stream, not system stream\n");
00499                 DONEBYTES (4);
00500             } else {
00501                 NEEDBYTES (6);
00502                 DONEBYTES (6);
00503                 bytes = (header[4] << 8) + header[5];
00504                 if (bytes > end - buf) {
00505                     state = DEMUX_SKIP;
00506                     state_bytes = bytes - (end - buf);
00507                     return 0;
00508                 }
00509                 buf += bytes;
00510             }
00511         }
00512     }
00513 }
00514 
00515 static void ps_loop (void)
00516 {
00517     uint8_t * end;
00518 
00519     do {
00520         end = buffer + fread (buffer, 1, BUFFER_SIZE, in_file);
00521         if (demux (buffer, end, 0))
00522             break;      /* hit program_end_code */
00523     } while (end == buffer + BUFFER_SIZE);
00524 }
00525 
00526 static int pva_demux (uint8_t * buf, uint8_t * end)
00527 {
00528     static int state = DEMUX_SKIP;
00529     static int state_bytes = 0;
00530     static uint8_t head_buf[12];
00531 
00532     uint8_t * header;
00533     int bytes;
00534     int len;
00535 
00536     switch (state) {
00537     case DEMUX_HEADER:
00538         if (state_bytes > 0) {
00539             header = head_buf;
00540             bytes = state_bytes;
00541             goto continue_header;
00542         }
00543         break;
00544     case DEMUX_DATA:
00545         if (state_bytes > end - buf) {
00546             decode_mpeg2 (buf, end);
00547             state_bytes -= end - buf;
00548             return 0;
00549         }
00550         decode_mpeg2 (buf, buf + state_bytes);
00551         buf += state_bytes;
00552         break;
00553     case DEMUX_SKIP:
00554         if (state_bytes > end - buf) {
00555             state_bytes -= end - buf;
00556             return 0;
00557         }
00558         buf += state_bytes;
00559         break;
00560     }
00561 
00562     while (1) {
00563     payload_start:
00564         header = buf;
00565         bytes = end - buf;
00566     continue_header:
00567         NEEDBYTES (2);
00568         if (header[0] != 0x41 || header[1] != 0x56) {
00569             if (header != head_buf) {
00570                 buf++;
00571                 goto payload_start;
00572             } else {
00573                 header[0] = header[1];
00574                 bytes = 1;
00575                 goto continue_header;
00576             }
00577         }
00578         NEEDBYTES (8);
00579         if (header[2] != 1) {
00580             DONEBYTES (8);
00581             bytes = (header[6] << 8) + header[7];
00582             if (bytes > end - buf) {
00583                 state = DEMUX_SKIP;
00584                 state_bytes = bytes - (end - buf);
00585                 return 0;
00586             } 
00587             buf += bytes; 
00588         } else {
00589             len = 8;
00590             if (header[5] & 0x10) {
00591                 len = 12 + (header[5] & 3);
00592                 NEEDBYTES (len);
00593                 decode_mpeg2 (header + 12, header + len);
00594                 mpeg2_pts (mpeg2dec, ((header[8] << 24) | (header[9] << 16) |
00595                                       (header[10] << 8) | header[11]));
00596             }
00597             DONEBYTES (len);
00598             bytes = (header[6] << 8) + header[7] + 8 - len;
00599             if (bytes > end - buf) {
00600                 decode_mpeg2 (buf, end);
00601                 state = DEMUX_DATA;
00602                 state_bytes = bytes - (end - buf);
00603                 return 0;
00604             } else if (bytes > 0) {
00605                 decode_mpeg2 (buf, buf + bytes);
00606                 buf += bytes;
00607             }
00608         }
00609     }
00610 }
00611 
00612 static void pva_loop (void)
00613 {
00614     uint8_t * end;
00615 
00616     do {
00617         end = buffer + fread (buffer, 1, BUFFER_SIZE, in_file);
00618         pva_demux (buffer, end);
00619     } while (end == buffer + BUFFER_SIZE);
00620 }
00621 
00622 static void ts_loop (void)
00623 {
00624 #define PACKETS (BUFFER_SIZE / 188)
00625     uint8_t * buf;
00626     uint8_t * data;
00627     uint8_t * end;
00628     int packets;
00629     int i;
00630     int pid;
00631 
00632     do {
00633         packets = fread (buffer, 188, PACKETS, in_file);
00634         for (i = 0; i < packets; i++) {
00635             buf = buffer + i * 188;
00636             end = buf + 188;
00637             if (buf[0] != 0x47) {
00638                 fprintf (stderr, "bad sync byte\n");
00639                 exit (1);
00640             }
00641             pid = ((buf[1] << 8) + buf[2]) & 0x1fff;
00642             if (pid != demux_pid)
00643                 continue;
00644             data = buf + 4;
00645             if (buf[3] & 0x20) {        /* buf contains an adaptation field */
00646                 data = buf + 5 + buf[4];
00647                 if (data > end)
00648                     continue;
00649             }
00650             if (buf[3] & 0x10)
00651                 demux (data, end, (buf[1] & 0x40) ? DEMUX_PAYLOAD_START : 0);
00652         }
00653     } while (packets == PACKETS);
00654 }
00655 
00656 static void es_loop (void)
00657 {
00658     uint8_t * end;
00659 
00660     do {
00661         end = buffer + fread (buffer, 1, BUFFER_SIZE, in_file);
00662         decode_mpeg2 (buffer, end);
00663     } while (end == buffer + BUFFER_SIZE);
00664 }
00665 
00666 int main (int argc, char ** argv)
00667 {
00668 #ifdef HAVE_IO_H
00669     setmode (fileno (stdout), O_BINARY);
00670 #endif
00671 
00672     fprintf (stderr, PACKAGE"-"VERSION
00673              " - by Michel Lespinasse <walken@zoy.org> and Aaron Holtzman\n");
00674 
00675     handle_args (argc, argv);
00676 
00677     output = output_open ();
00678     if (output == NULL) {
00679         fprintf (stderr, "Can not open output\n");
00680         return 1;
00681     }
00682     mpeg2dec = mpeg2_init ();
00683     if (mpeg2dec == NULL)
00684         exit (1);
00685 
00686     if (demux_pva)
00687         pva_loop ();
00688     else if (demux_pid)
00689         ts_loop ();
00690     else if (demux_track)
00691         ps_loop ();
00692     else
00693         es_loop ();
00694 
00695     mpeg2_close (mpeg2dec);
00696     if (output->close)
00697         output->close (output);
00698     print_fps (1);
00699     return 0;
00700 }
 

Powered by Plone

This site conforms to the following standards: